1 /* 2 * Copyright (C) 2019 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 17 package android.media.extractor.cts; 18 19 import static android.mediav2.common.cts.CodecTestBase.hasDecoder; 20 import static android.mediav2.common.cts.CodecTestBase.isExtractorFormatSimilar; 21 22 import static org.junit.Assert.assertEquals; 23 import static org.junit.Assert.assertTrue; 24 import static org.junit.Assert.fail; 25 import static org.junit.Assume.assumeTrue; 26 27 import android.content.Context; 28 import android.content.res.AssetFileDescriptor; 29 import android.media.MediaCodec; 30 import android.media.MediaCodecInfo; 31 import android.media.MediaDataSource; 32 import android.media.MediaExtractor; 33 import android.media.MediaFormat; 34 import android.media.cts.TestMediaDataSource; 35 import android.mediav2.common.cts.CodecDecoderTestBase; 36 import android.mediav2.common.cts.CodecTestBase; 37 import android.net.Uri; 38 import android.os.ParcelFileDescriptor; 39 import android.os.PersistableBundle; 40 import android.util.Log; 41 import android.webkit.cts.CtsTestServer; 42 43 import androidx.test.filters.LargeTest; 44 import androidx.test.filters.SmallTest; 45 import androidx.test.platform.app.InstrumentationRegistry; 46 47 import com.android.compatibility.common.util.ApiTest; 48 import com.android.compatibility.common.util.CddTest; 49 import com.android.compatibility.common.util.Preconditions; 50 51 import org.apache.http.Header; 52 import org.apache.http.HttpRequest; 53 import org.junit.After; 54 import org.junit.Before; 55 import org.junit.Ignore; 56 import org.junit.Rule; 57 import org.junit.Test; 58 import org.junit.experimental.runners.Enclosed; 59 import org.junit.rules.TestName; 60 import org.junit.runner.RunWith; 61 import org.junit.runners.Parameterized; 62 63 import java.io.BufferedReader; 64 import java.io.File; 65 import java.io.FileInputStream; 66 import java.io.FileOutputStream; 67 import java.io.IOException; 68 import java.io.InputStreamReader; 69 import java.io.Reader; 70 import java.io.StreamTokenizer; 71 import java.nio.ByteBuffer; 72 import java.util.ArrayList; 73 import java.util.Arrays; 74 import java.util.Collection; 75 import java.util.Collections; 76 import java.util.HashMap; 77 import java.util.List; 78 import java.util.Map; 79 import java.util.Random; 80 import java.util.zip.CRC32; 81 82 @RunWith(Enclosed.class) 83 public class ExtractorTest { 84 private static final String LOG_TAG = ExtractorTest.class.getSimpleName(); 85 private static final boolean ENABLE_LOGS = false; 86 private static final int MAX_SAMPLE_SIZE = 4 * 1024 * 1024; 87 private static final String EXT_SEL_KEY = "ext-sel"; 88 static private final List<String> codecListforTypeMp4 = 89 Arrays.asList(MediaFormat.MIMETYPE_AUDIO_MPEG, MediaFormat.MIMETYPE_AUDIO_AAC, 90 MediaFormat.MIMETYPE_AUDIO_FLAC, MediaFormat.MIMETYPE_AUDIO_VORBIS, 91 MediaFormat.MIMETYPE_AUDIO_OPUS, MediaFormat.MIMETYPE_VIDEO_MPEG2, 92 MediaFormat.MIMETYPE_VIDEO_MPEG4, MediaFormat.MIMETYPE_VIDEO_H263, 93 MediaFormat.MIMETYPE_VIDEO_AVC, MediaFormat.MIMETYPE_VIDEO_HEVC); 94 static private final List<String> codecListforTypeWebm = 95 Arrays.asList(MediaFormat.MIMETYPE_AUDIO_VORBIS, MediaFormat.MIMETYPE_AUDIO_OPUS, 96 MediaFormat.MIMETYPE_VIDEO_VP8, MediaFormat.MIMETYPE_VIDEO_VP9); 97 static private final List<String> codecListforType3gp = 98 Arrays.asList(MediaFormat.MIMETYPE_AUDIO_AAC, MediaFormat.MIMETYPE_AUDIO_AMR_NB, 99 MediaFormat.MIMETYPE_AUDIO_AMR_WB, MediaFormat.MIMETYPE_VIDEO_MPEG4, 100 MediaFormat.MIMETYPE_VIDEO_H263, MediaFormat.MIMETYPE_VIDEO_AVC); 101 static private final List<String> codecListforTypeMkv = 102 Arrays.asList(MediaFormat.MIMETYPE_AUDIO_MPEG, MediaFormat.MIMETYPE_AUDIO_AAC, 103 MediaFormat.MIMETYPE_AUDIO_FLAC, MediaFormat.MIMETYPE_AUDIO_VORBIS, 104 MediaFormat.MIMETYPE_AUDIO_OPUS, MediaFormat.MIMETYPE_VIDEO_MPEG2, 105 MediaFormat.MIMETYPE_VIDEO_MPEG4, MediaFormat.MIMETYPE_VIDEO_H263, 106 MediaFormat.MIMETYPE_VIDEO_AVC, MediaFormat.MIMETYPE_VIDEO_HEVC, 107 MediaFormat.MIMETYPE_VIDEO_VP8, MediaFormat.MIMETYPE_VIDEO_VP9); 108 static private final List<String> codecListforTypeOgg = 109 Arrays.asList(MediaFormat.MIMETYPE_AUDIO_VORBIS, MediaFormat.MIMETYPE_AUDIO_OPUS); 110 static private final List<String> codecListforTypeTs = 111 Arrays.asList(MediaFormat.MIMETYPE_AUDIO_AAC, MediaFormat.MIMETYPE_VIDEO_MPEG2, 112 MediaFormat.MIMETYPE_VIDEO_AVC); 113 static private final List<String> codecListforTypePs = 114 Arrays.asList(MediaFormat.MIMETYPE_VIDEO_MPEG2); 115 static private final List<String> codecListforTypeRaw = 116 Arrays.asList(MediaFormat.MIMETYPE_AUDIO_AAC, MediaFormat.MIMETYPE_AUDIO_FLAC, 117 MediaFormat.MIMETYPE_AUDIO_MPEG, MediaFormat.MIMETYPE_AUDIO_AMR_NB, 118 MediaFormat.MIMETYPE_AUDIO_AMR_WB, MediaFormat.MIMETYPE_AUDIO_RAW); 119 static private final List<String> codecListforTypeWav = 120 Arrays.asList(MediaFormat.MIMETYPE_AUDIO_RAW, MediaFormat.MIMETYPE_AUDIO_G711_ALAW, 121 MediaFormat.MIMETYPE_AUDIO_G711_MLAW, MediaFormat.MIMETYPE_AUDIO_MSGSM); 122 // List of codecs that are not required to be supported as per CDD but are tested 123 static private final List<String> codecListSupp = 124 Arrays.asList(MediaFormat.MIMETYPE_VIDEO_AV1, MediaFormat.MIMETYPE_AUDIO_AC3, 125 MediaFormat.MIMETYPE_AUDIO_AC4, MediaFormat.MIMETYPE_AUDIO_EAC3); 126 private static final String MEDIA_DIR = WorkDir.getMediaDirString(); 127 private static String extSel; 128 129 static { 130 android.os.Bundle args = InstrumentationRegistry.getArguments(); 131 final String defSel = "mp4;webm;3gp;mkv;ogg;supp;raw;ts;ps;wav"; 132 extSel = (null == args.getString(EXT_SEL_KEY)) ? defSel : args.getString(EXT_SEL_KEY); 133 } 134 shouldRunTest(String mediaType)135 private static boolean shouldRunTest(String mediaType) { 136 boolean result = false; 137 if ((extSel.contains("mp4") && codecListforTypeMp4.contains(mediaType)) 138 || (extSel.contains("webm") && codecListforTypeWebm.contains(mediaType)) 139 || (extSel.contains("3gp") && codecListforType3gp.contains(mediaType)) 140 || (extSel.contains("mkv") && codecListforTypeMkv.contains(mediaType)) 141 || (extSel.contains("ogg") && codecListforTypeOgg.contains(mediaType)) 142 || (extSel.contains("ts") && codecListforTypeTs.contains(mediaType)) 143 || (extSel.contains("ps") && codecListforTypePs.contains(mediaType)) 144 || (extSel.contains("raw") && codecListforTypeRaw.contains(mediaType)) 145 || (extSel.contains("wav") && codecListforTypeWav.contains(mediaType)) 146 || (extSel.contains("supp") && codecListSupp.contains(mediaType))) { 147 result = true; 148 } 149 return result; 150 } 151 isExtractorOKonEOS(MediaExtractor extractor)152 private static boolean isExtractorOKonEOS(MediaExtractor extractor) { 153 return extractor.getSampleTrackIndex() < 0 && extractor.getSampleSize() < 0 && 154 extractor.getSampleFlags() < 0 && extractor.getSampleTime() < 0; 155 } 156 isSampleInfoIdentical(MediaCodec.BufferInfo refSample, MediaCodec.BufferInfo testSample)157 private static boolean isSampleInfoIdentical(MediaCodec.BufferInfo refSample, 158 MediaCodec.BufferInfo testSample) { 159 return refSample.flags == testSample.flags && refSample.size == testSample.size && 160 refSample.presentationTimeUs == testSample.presentationTimeUs; 161 } 162 isSampleInfoValidAndIdentical(MediaCodec.BufferInfo refSample, MediaCodec.BufferInfo testSample)163 private static boolean isSampleInfoValidAndIdentical(MediaCodec.BufferInfo refSample, 164 MediaCodec.BufferInfo testSample) { 165 return refSample.flags == testSample.flags && refSample.size == testSample.size && 166 Math.abs(refSample.presentationTimeUs - testSample.presentationTimeUs) <= 1 && 167 refSample.flags >= 0 && refSample.size >= 0 && refSample.presentationTimeUs >= 0; 168 } 169 isMediaSimilar(MediaExtractor refExtractor, MediaExtractor testExtractor, String mediaType, int sampleLimit)170 private static boolean isMediaSimilar(MediaExtractor refExtractor, MediaExtractor testExtractor, 171 String mediaType, int sampleLimit) { 172 ByteBuffer refBuffer = ByteBuffer.allocate(MAX_SAMPLE_SIZE); 173 ByteBuffer testBuffer = ByteBuffer.allocate(MAX_SAMPLE_SIZE); 174 175 int noOfTracksMatched = 0; 176 for (int refTrackID = 0; refTrackID < refExtractor.getTrackCount(); refTrackID++) { 177 MediaFormat refFormat = refExtractor.getTrackFormat(refTrackID); 178 String refMediaType = refFormat.getString(MediaFormat.KEY_MIME); 179 if (mediaType != null && !refMediaType.equals(mediaType)) { 180 continue; 181 } 182 for (int testTrackID = 0; testTrackID < testExtractor.getTrackCount(); testTrackID++) { 183 MediaFormat testFormat = testExtractor.getTrackFormat(testTrackID); 184 if (!isExtractorFormatSimilar(refFormat, testFormat)) { 185 continue; 186 } 187 refExtractor.selectTrack(refTrackID); 188 testExtractor.selectTrack(testTrackID); 189 190 MediaCodec.BufferInfo refSampleInfo = new MediaCodec.BufferInfo(); 191 MediaCodec.BufferInfo testSampleInfo = new MediaCodec.BufferInfo(); 192 boolean areTracksIdentical = true; 193 for (int frameCount = 0; ; frameCount++) { 194 refSampleInfo.set(0, (int) refExtractor.getSampleSize(), 195 refExtractor.getSampleTime(), refExtractor.getSampleFlags()); 196 testSampleInfo.set(0, (int) testExtractor.getSampleSize(), 197 testExtractor.getSampleTime(), testExtractor.getSampleFlags()); 198 if (!isSampleInfoValidAndIdentical(refSampleInfo, testSampleInfo)) { 199 if (ENABLE_LOGS) { 200 Log.d(LOG_TAG, 201 " Mediatype: " + refMediaType + " mismatch for sample: " 202 + frameCount); 203 Log.d(LOG_TAG, " flags exp/got: " + refSampleInfo.flags + '/' 204 + testSampleInfo.flags); 205 Log.d(LOG_TAG, " size exp/got: " + refSampleInfo.size + '/' 206 + testSampleInfo.size); 207 Log.d(LOG_TAG, " ts exp/got: " + refSampleInfo.presentationTimeUs 208 + '/' + testSampleInfo.presentationTimeUs); 209 } 210 areTracksIdentical = false; 211 break; 212 } 213 int refSz = refExtractor.readSampleData(refBuffer, 0); 214 if (refSz != refSampleInfo.size) { 215 if (ENABLE_LOGS) { 216 Log.d(LOG_TAG, "Mediatype: " + refMediaType + " Size exp/got: " 217 + refSampleInfo.size + '/' + refSz); 218 } 219 areTracksIdentical = false; 220 break; 221 } 222 int testSz = testExtractor.readSampleData(testBuffer, 0); 223 if (testSz != testSampleInfo.size) { 224 if (ENABLE_LOGS) { 225 Log.d(LOG_TAG, "Mediatype: " + refMediaType + " Size exp/got: " 226 + testSampleInfo.size + '/' + testSz); 227 } 228 areTracksIdentical = false; 229 break; 230 } 231 int trackIndex = refExtractor.getSampleTrackIndex(); 232 if (trackIndex != refTrackID) { 233 if (ENABLE_LOGS) { 234 Log.d(LOG_TAG, "Mediatype: " + refMediaType + " TrackID exp/got: " 235 + refTrackID + '/' + trackIndex); 236 } 237 areTracksIdentical = false; 238 break; 239 } 240 trackIndex = testExtractor.getSampleTrackIndex(); 241 if (trackIndex != testTrackID) { 242 if (ENABLE_LOGS) { 243 Log.d(LOG_TAG, "Mediatype: " + refMediaType + " TrackID exp/got: " 244 + testTrackID + '/' + trackIndex); 245 } 246 areTracksIdentical = false; 247 break; 248 } 249 if (!testBuffer.equals(refBuffer)) { 250 if (ENABLE_LOGS) { 251 Log.d(LOG_TAG, 252 "Mediatype: " + refMediaType + " sample data is not identical"); 253 } 254 areTracksIdentical = false; 255 break; 256 } 257 boolean haveRefSamples = refExtractor.advance(); 258 boolean haveTestSamples = testExtractor.advance(); 259 if (haveRefSamples != haveTestSamples) { 260 if (ENABLE_LOGS) { 261 Log.d(LOG_TAG, "Mediatype: " + refMediaType + " Mismatch " 262 + "in sampleCount"); 263 } 264 areTracksIdentical = false; 265 break; 266 } 267 268 if (!haveRefSamples && !isExtractorOKonEOS(refExtractor)) { 269 if (ENABLE_LOGS) { 270 Log.d(LOG_TAG, 271 "Mediatype: " + refMediaType 272 + " calls post advance() are not OK"); 273 } 274 areTracksIdentical = false; 275 break; 276 } 277 if (!haveTestSamples && !isExtractorOKonEOS(testExtractor)) { 278 if (ENABLE_LOGS) { 279 Log.d(LOG_TAG, 280 "Mediatype: " + refMediaType 281 + " calls post advance() are not OK"); 282 } 283 areTracksIdentical = false; 284 break; 285 } 286 if (ENABLE_LOGS) { 287 Log.v(LOG_TAG, "Mediatype: " + refMediaType + " Sample: " + frameCount 288 + " flags: " + refSampleInfo.flags + " size: " + refSampleInfo.size 289 + " ts: " + refSampleInfo.presentationTimeUs); 290 } 291 if (!haveRefSamples || frameCount >= sampleLimit) { 292 break; 293 } 294 } 295 testExtractor.unselectTrack(testTrackID); 296 refExtractor.unselectTrack(refTrackID); 297 if (areTracksIdentical) { 298 noOfTracksMatched++; 299 break; 300 } 301 } 302 if (mediaType != null && noOfTracksMatched > 0) break; 303 } 304 if (mediaType == null) { 305 return noOfTracksMatched == refExtractor.getTrackCount(); 306 } else { 307 return noOfTracksMatched > 0; 308 } 309 } 310 readAllData(MediaExtractor extractor, String mediaType, int sampleLimit)311 private static long readAllData(MediaExtractor extractor, String mediaType, int sampleLimit) { 312 CRC32 checksum = new CRC32(); 313 ByteBuffer buffer = ByteBuffer.allocate(MAX_SAMPLE_SIZE); 314 int tracksSelected = 0; 315 for (int trackID = 0; trackID < extractor.getTrackCount(); trackID++) { 316 MediaFormat format = extractor.getTrackFormat(trackID); 317 String srcMediaType = format.getString(MediaFormat.KEY_MIME); 318 if (mediaType != null && !srcMediaType.equals(mediaType)) { 319 continue; 320 } 321 extractor.selectTrack(trackID); 322 tracksSelected++; 323 if (srcMediaType.startsWith("audio/")) { 324 buffer.putInt(0); 325 buffer.putInt(format.getInteger(MediaFormat.KEY_SAMPLE_RATE)); 326 buffer.putInt(format.getInteger(MediaFormat.KEY_CHANNEL_COUNT)); 327 } else if (srcMediaType.startsWith("video/")) { 328 buffer.putInt(1); 329 buffer.putInt(format.getInteger(MediaFormat.KEY_WIDTH)); 330 buffer.putInt(format.getInteger(MediaFormat.KEY_HEIGHT)); 331 } else { 332 buffer.putInt(2); 333 } 334 buffer.putLong(format.getLong(MediaFormat.KEY_DURATION)); 335 for (int i = 0; ; i++) { 336 String csdKey = "csd-" + i; 337 if (format.containsKey(csdKey)) { 338 checksum.update(format.getByteBuffer(csdKey)); 339 } else break; 340 } 341 } 342 assertTrue(tracksSelected > 0); 343 buffer.flip(); 344 checksum.update(buffer); 345 346 MediaCodec.BufferInfo sampleInfo = new MediaCodec.BufferInfo(); 347 for (int sampleCount = 0; sampleCount < sampleLimit; sampleCount++) { 348 sampleInfo.set(0, (int) extractor.getSampleSize(), extractor.getSampleTime(), 349 extractor.getSampleFlags()); 350 extractor.readSampleData(buffer, 0); 351 checksum.update(buffer); 352 assertEquals(sampleInfo.size, buffer.limit()); 353 assertTrue(sampleInfo.flags != -1); 354 assertTrue(sampleInfo.presentationTimeUs != -1); 355 buffer.position(0); 356 buffer.putInt(sampleInfo.size) 357 .putInt(sampleInfo.flags) 358 .putLong(sampleInfo.presentationTimeUs); 359 buffer.flip(); 360 checksum.update(buffer); 361 sampleCount++; 362 if (!extractor.advance()) { 363 assertTrue(isExtractorOKonEOS(extractor)); 364 break; 365 } 366 } 367 for (int trackID = 0; trackID < extractor.getTrackCount(); trackID++) { 368 extractor.unselectTrack(trackID); 369 } 370 return checksum.getValue(); 371 } 372 nativeReadAllData(String srcPath, String mediaType, int sampleLimit, String[] keys, String[] values, boolean isSrcUrl)373 private static native long nativeReadAllData(String srcPath, String mediaType, int sampleLimit, 374 String[] keys, String[] values, boolean isSrcUrl); 375 376 /** 377 * Tests setDataSource(...) Api by observing the extractor behavior after its successful 378 * instantiation using a media stream. 379 */ 380 @ApiTest(apis = {"android.media.MediaExtractor#setDataSource", "AMediaExtractor_setDataSource"}) 381 @SmallTest 382 public static class SetDataSourceTest { 383 @Rule 384 public TestName testName = new TestName(); 385 386 private static final String INPUT_MEDIA = "ForBiggerEscapes.mp4"; 387 private static final String RES_STRING = "raw/forbiggerescapes"; 388 private CtsTestServer mWebServer; 389 private String mInpMediaUrl; 390 private MediaExtractor mRefExtractor; 391 392 static { 393 System.loadLibrary("ctsmediaextractor_jni"); 394 } 395 396 @Before setUp()397 public void setUp() throws IOException { 398 mRefExtractor = new MediaExtractor(); 399 Preconditions.assertTestFileExists(MEDIA_DIR + INPUT_MEDIA); 400 mRefExtractor.setDataSource(MEDIA_DIR + INPUT_MEDIA); 401 try { 402 Context context = InstrumentationRegistry.getInstrumentation().getTargetContext(); 403 mWebServer = new CtsTestServer(context); 404 mInpMediaUrl = mWebServer.getAssetUrl(RES_STRING); 405 } catch (Exception e) { 406 fail(e.getMessage()); 407 } 408 } 409 410 @After tearDown()411 public void tearDown() { 412 mRefExtractor.release(); 413 mRefExtractor = null; 414 mWebServer.shutdown(); 415 } 416 areMetricsIdentical(MediaExtractor refExtractor, MediaExtractor testExtractor)417 private static boolean areMetricsIdentical(MediaExtractor refExtractor, 418 MediaExtractor testExtractor) { 419 PersistableBundle bundle = refExtractor.getMetrics(); 420 int refNumTracks = bundle.getInt(MediaExtractor.MetricsConstants.TRACKS); 421 String refFormat = bundle.getString(MediaExtractor.MetricsConstants.FORMAT); 422 String refMediaType = bundle.getString(MediaExtractor.MetricsConstants.MIME_TYPE); 423 bundle = testExtractor.getMetrics(); 424 int testNumTracks = bundle.getInt(MediaExtractor.MetricsConstants.TRACKS); 425 String testFormat = bundle.getString(MediaExtractor.MetricsConstants.FORMAT); 426 String testMediaType = bundle.getString(MediaExtractor.MetricsConstants.MIME_TYPE); 427 boolean result = testNumTracks == refNumTracks && testFormat.equals(refFormat) && 428 testMediaType.equals(refMediaType); 429 if (ENABLE_LOGS) { 430 Log.d(LOG_TAG, " NumTracks exp/got: " + refNumTracks + '/' + testNumTracks); 431 Log.d(LOG_TAG, " Format exp/got: " + refFormat + '/' + testFormat); 432 Log.d(LOG_TAG, " Mediatype exp/got: " + refMediaType + '/' + testMediaType); 433 } 434 return result; 435 } 436 isSeekOk(MediaExtractor refExtractor, MediaExtractor testExtractor)437 private static boolean isSeekOk(MediaExtractor refExtractor, MediaExtractor testExtractor) { 438 final long maxEstDuration = 14000000; 439 final int MAX_SEEK_POINTS = 7; 440 final long mSeed = 0x12b9b0a1; 441 final Random randNum = new Random(mSeed); 442 MediaCodec.BufferInfo refSampleInfo = new MediaCodec.BufferInfo(); 443 MediaCodec.BufferInfo testSampleInfo = new MediaCodec.BufferInfo(); 444 boolean result = true; 445 for (int trackID = 0; trackID < refExtractor.getTrackCount() && result; trackID++) { 446 refExtractor.selectTrack(trackID); 447 testExtractor.selectTrack(trackID); 448 for (int i = 0; i < MAX_SEEK_POINTS && result; i++) { 449 long pts = (long) (randNum.nextDouble() * maxEstDuration); 450 for (int mode = MediaExtractor.SEEK_TO_PREVIOUS_SYNC; 451 mode <= MediaExtractor.SEEK_TO_CLOSEST_SYNC; mode++) { 452 refExtractor.seekTo(pts, mode); 453 testExtractor.seekTo(pts, mode); 454 refSampleInfo.set(0, (int) refExtractor.getSampleSize(), 455 refExtractor.getSampleTime(), refExtractor.getSampleFlags()); 456 testSampleInfo.set(0, (int) testExtractor.getSampleSize(), 457 testExtractor.getSampleTime(), testExtractor.getSampleFlags()); 458 result = isSampleInfoIdentical(refSampleInfo, testSampleInfo); 459 int refTrackIdx = refExtractor.getSampleTrackIndex(); 460 int testTrackIdx = testExtractor.getSampleTrackIndex(); 461 result &= (refTrackIdx == testTrackIdx); 462 if (ENABLE_LOGS) { 463 Log.d(LOG_TAG, " mode/pts/trackId:" + mode + "/" + pts + "/" + trackID); 464 Log.d(LOG_TAG, " trackId exp/got: " + refTrackIdx + '/' + testTrackIdx); 465 Log.d(LOG_TAG, " flags exp/got: " + 466 refSampleInfo.flags + '/' + testSampleInfo.flags); 467 Log.d(LOG_TAG, " size exp/got: " + 468 refSampleInfo.size + '/' + testSampleInfo.size); 469 Log.d(LOG_TAG, " ts exp/got: " + refSampleInfo.presentationTimeUs + 470 '/' + testSampleInfo.presentationTimeUs); 471 } 472 } 473 } 474 refExtractor.unselectTrack(trackID); 475 testExtractor.unselectTrack(trackID); 476 } 477 return result; 478 } 479 480 @Test testAssetFD()481 public void testAssetFD() throws IOException { 482 Preconditions.assertTestFileExists(MEDIA_DIR + INPUT_MEDIA); 483 File inpFile = new File(MEDIA_DIR + INPUT_MEDIA); 484 MediaExtractor testExtractor = new MediaExtractor(); 485 try (ParcelFileDescriptor parcelFD = ParcelFileDescriptor 486 .open(inpFile, ParcelFileDescriptor.MODE_READ_ONLY); 487 AssetFileDescriptor afd = new AssetFileDescriptor(parcelFD, 0, 488 AssetFileDescriptor.UNKNOWN_LENGTH)) { 489 testExtractor.setDataSource(afd); 490 } 491 assertTrue(testExtractor.getCachedDuration() < 0); 492 if (!isMediaSimilar(mRefExtractor, testExtractor, null, Integer.MAX_VALUE) || 493 !areMetricsIdentical(mRefExtractor, testExtractor) || 494 !isSeekOk(mRefExtractor, testExtractor)) { 495 fail("setDataSource failed: " + testName.getMethodName()); 496 } 497 testExtractor.release(); 498 } 499 500 @Test 501 public void testFileDescriptor() throws IOException { 502 Preconditions.assertTestFileExists(MEDIA_DIR + INPUT_MEDIA); 503 File inpFile = new File(MEDIA_DIR + INPUT_MEDIA); 504 MediaExtractor testExtractor = new MediaExtractor(); 505 try (FileInputStream fInp = new FileInputStream(inpFile)) { 506 testExtractor.setDataSource(fInp.getFD()); 507 } 508 assertTrue(testExtractor.getCachedDuration() < 0); 509 if (!isMediaSimilar(mRefExtractor, testExtractor, null, Integer.MAX_VALUE) || 510 !areMetricsIdentical(mRefExtractor, testExtractor) || 511 !isSeekOk(mRefExtractor, testExtractor)) { 512 fail("setDataSource failed: " + testName.getMethodName()); 513 } 514 long sdkChecksum = readAllData(testExtractor, null, Integer.MAX_VALUE); 515 long ndkChecksum = nativeReadAllData(MEDIA_DIR + INPUT_MEDIA, "", 516 Integer.MAX_VALUE, null, null, false); 517 testExtractor.release(); 518 assertEquals("SDK and NDK checksums mismatch", sdkChecksum, ndkChecksum); 519 } 520 521 @Test 522 public void testFileDescriptorLenOffset() throws IOException { 523 Preconditions.assertTestFileExists(MEDIA_DIR + INPUT_MEDIA); 524 File inpFile = new File(MEDIA_DIR + INPUT_MEDIA); 525 File outFile = File.createTempFile("temp", ".out"); 526 byte[] garbageAppend = "PrefixGarbage".getBytes(); 527 try (FileInputStream fInp = new FileInputStream(inpFile); 528 FileOutputStream fOut = new FileOutputStream(outFile)) { 529 fOut.write(garbageAppend); 530 byte[] data = new byte[(int) new File(inpFile.toString()).length()]; 531 if (fInp.read(data) == -1) { 532 fail("Failed to read input file"); 533 } 534 fOut.write(data); 535 fOut.write(garbageAppend); 536 } 537 MediaExtractor testExtractor = new MediaExtractor(); 538 try (FileInputStream fInp = new FileInputStream(outFile)) { 539 testExtractor.setDataSource(fInp.getFD(), garbageAppend.length, 540 inpFile.length()); 541 } 542 assertTrue(testExtractor.getCachedDuration() < 0); 543 if (!isMediaSimilar(mRefExtractor, testExtractor, null, Integer.MAX_VALUE) || 544 !areMetricsIdentical(mRefExtractor, testExtractor) || 545 !isSeekOk(mRefExtractor, testExtractor)) { 546 fail("setDataSource failed: " + testName.getMethodName()); 547 } 548 testExtractor.release(); 549 outFile.delete(); 550 } 551 552 @Test 553 public void testMediaDataSource() throws Exception { 554 Preconditions.assertTestFileExists(MEDIA_DIR + INPUT_MEDIA); 555 TestMediaDataSource dataSource = 556 TestMediaDataSource.fromString(MEDIA_DIR + INPUT_MEDIA, false, false); 557 MediaExtractor testExtractor = new MediaExtractor(); 558 testExtractor.setDataSource(dataSource); 559 assertTrue(testExtractor.getCachedDuration() < 0); 560 if (!isMediaSimilar(mRefExtractor, testExtractor, null, Integer.MAX_VALUE) || 561 !areMetricsIdentical(mRefExtractor, testExtractor) || 562 !isSeekOk(mRefExtractor, testExtractor)) { 563 fail("setDataSource failed: " + testName.getMethodName()); 564 } 565 testExtractor.release(); 566 assertTrue(dataSource.isClosed()); 567 } 568 569 @Test 570 public void testContextUri() throws IOException { 571 Context context = InstrumentationRegistry.getInstrumentation().getContext(); 572 String path = "android.resource://android.media.extractor.cts/" + RES_STRING; 573 MediaExtractor testExtractor = new MediaExtractor(); 574 testExtractor.setDataSource(context, Uri.parse(path), null); 575 assertTrue(testExtractor.getCachedDuration() < 0); 576 if (!isMediaSimilar(mRefExtractor, testExtractor, null, Integer.MAX_VALUE) || 577 !areMetricsIdentical(mRefExtractor, testExtractor) || 578 !isSeekOk(mRefExtractor, testExtractor)) { 579 fail("setDataSource failed: " + testName.getMethodName()); 580 } 581 testExtractor.release(); 582 } 583 584 private void checkExtractorOkForUrlDS(Map<String, String> headers) throws Exception { 585 MediaExtractor testExtractor = new MediaExtractor(); 586 testExtractor.setDataSource(mInpMediaUrl, headers); 587 HttpRequest req = mWebServer.getLastAssetRequest(RES_STRING); 588 if (headers != null) { 589 for (String key : headers.keySet()) { 590 String value = headers.get(key); 591 Header[] header = req.getHeaders(key); 592 assertTrue( 593 "expecting " + key + ":" + value + ", saw " + Arrays.toString(header), 594 header.length == 1 && header[0].getValue().equals(value)); 595 } 596 } 597 if (!isMediaSimilar(mRefExtractor, testExtractor, null, Integer.MAX_VALUE) || 598 !areMetricsIdentical(mRefExtractor, testExtractor) || 599 !isSeekOk(mRefExtractor, testExtractor)) { 600 fail("setDataSource failed: " + testName.getMethodName()); 601 } 602 testExtractor.selectTrack(0); 603 for (int idx = 0; ; idx++) { 604 if ((idx & (idx - 1)) == 0) { 605 long cachedDuration = testExtractor.getCachedDuration(); 606 if (ENABLE_LOGS) { 607 Log.v(LOG_TAG, "cachedDuration at frame: " + idx + " is:" + cachedDuration); 608 } 609 assertTrue("cached duration should be non-negative", cachedDuration >= 0); 610 } 611 if (!testExtractor.advance()) break; 612 } 613 assertTrue(testExtractor.hasCacheReachedEndOfStream()); 614 testExtractor.unselectTrack(0); 615 testExtractor.release(); 616 } 617 618 @Test 619 public void testUrlDataSource() throws Exception { 620 checkExtractorOkForUrlDS(null); 621 622 Map<String, String> headers = new HashMap<>(); 623 checkExtractorOkForUrlDS(headers); 624 625 String[] keys = new String[]{"From", "Client", "Location"}; 626 String[] values = new String[]{"alcor@bigdipper.asm", "CtsTestServer", "UrsaMajor"}; 627 for (int i = 0; i < keys.length; i++) { 628 headers.put(keys[i], values[i]); 629 } 630 checkExtractorOkForUrlDS(headers); 631 632 MediaExtractor testExtractor = new MediaExtractor(); 633 testExtractor.setDataSource(mInpMediaUrl, headers); 634 long sdkChecksum = readAllData(testExtractor, null, Integer.MAX_VALUE); 635 testExtractor.release(); 636 long ndkChecksum = nativeReadAllData(mInpMediaUrl, "", Integer.MAX_VALUE, keys, 637 values, true); 638 assertEquals("SDK and NDK checksums mismatch", sdkChecksum, ndkChecksum); 639 ndkChecksum = nativeReadAllData(mInpMediaUrl, "", Integer.MAX_VALUE, new String[0], 640 new String[0], true); 641 assertEquals("SDK and NDK checksums mismatch", sdkChecksum, ndkChecksum); 642 } 643 644 private native boolean nativeTestDataSource(String srcPath, String srcUrl); 645 646 @Test 647 public void testDataSourceNative() { 648 Preconditions.assertTestFileExists(MEDIA_DIR + INPUT_MEDIA); 649 assertTrue(testName.getMethodName() + " failed ", 650 nativeTestDataSource(MEDIA_DIR + INPUT_MEDIA, mInpMediaUrl)); 651 } 652 } 653 654 /** 655 * Encloses extractor functionality tests 656 */ 657 @RunWith(Parameterized.class) 658 public static class FunctionalityTest { 659 private static final int MAX_SEEK_POINTS = 7; 660 private static final long SEED = 0x12b9b0a1; 661 private final Random mRandNum = new Random(SEED); 662 private String[] mSrcFiles; 663 private String mMediaType; 664 665 static { 666 System.loadLibrary("ctsmediaextractor_jni"); 667 } 668 669 @Rule 670 public TestName testName = new TestName(); 671 672 @Parameterized.Parameters(name = "{index}_{0}") 673 public static Collection<Object[]> input() { 674 return Arrays.asList(new Object[][]{ 675 {MediaFormat.MIMETYPE_VIDEO_MPEG2, new String[]{ 676 "bbb_cif_768kbps_30fps_mpeg2_stereo_48kHz_192kbps_mp3.mp4", 677 "bbb_cif_768kbps_30fps_mpeg2.mkv", 678 /* TODO(b/162919907) 679 "bbb_cif_768kbps_30fps_mpeg2.vob",*/ 680 /* TODO(b/162715861) 681 "bbb_cif_768kbps_30fps_mpeg2.ts" */}}, 682 {MediaFormat.MIMETYPE_VIDEO_H263, new String[]{ 683 "bbb_cif_768kbps_30fps_h263.mp4", 684 "bbb_cif_768kbps_30fps_h263_stereo_48kHz_192kbps_flac.mkv", 685 "bbb_cif_768kbps_30fps_h263_mono_8kHz_12kbps_amrnb.3gp",}}, 686 {MediaFormat.MIMETYPE_VIDEO_MPEG4, new String[]{ 687 "bbb_cif_768kbps_30fps_mpeg4.mkv", 688 "bbb_cif_768kbps_30fps_mpeg4_stereo_48kHz_192kbps_flac.mp4", 689 "bbb_cif_768kbps_30fps_mpeg4_mono_16kHz_20kbps_amrwb.3gp",}}, 690 {MediaFormat.MIMETYPE_VIDEO_AVC, new String[]{ 691 "bbb_cif_768kbps_30fps_avc_stereo_48kHz_192kbps_vorbis.mp4", 692 "bbb_cif_768kbps_30fps_avc_stereo_48kHz_192kbps_aac.mkv", 693 "bbb_cif_768kbps_30fps_avc_stereo_48kHz_192kbps_aac.3gp", 694 /* TODO(b/162715861) 695 "bbb_cif_768kbps_30fps_avc.ts",*/}}, 696 {MediaFormat.MIMETYPE_VIDEO_HEVC, new String[]{ 697 "bbb_cif_768kbps_30fps_hevc_stereo_48kHz_192kbps_opus.mp4", 698 "bbb_cif_768kbps_30fps_hevc_stereo_48kHz_192kbps_mp3.mkv",}}, 699 {MediaFormat.MIMETYPE_VIDEO_VP8, new String[]{ 700 "bbb_cif_768kbps_30fps_vp8_stereo_48kHz_192kbps_vorbis.webm", 701 "bbb_cif_768kbps_30fps_vp8_stereo_48kHz_192kbps_vorbis.mkv"}}, 702 {MediaFormat.MIMETYPE_VIDEO_VP9, new String[]{ 703 "bbb_cif_768kbps_30fps_vp9_stereo_48kHz_192kbps_opus.webm", 704 "bbb_cif_768kbps_30fps_vp9_stereo_48kHz_192kbps_opus.mkv",}}, 705 {MediaFormat.MIMETYPE_VIDEO_AV1, new String[]{ 706 "bbb_cif_768kbps_30fps_av1.mp4", 707 "bbb_cif_768kbps_30fps_av1.webm", 708 "bbb_cif_768kbps_30fps_av1.mkv",}}, 709 {MediaFormat.MIMETYPE_AUDIO_VORBIS, new String[]{ 710 "bbb_cif_768kbps_30fps_avc_stereo_48kHz_192kbps_vorbis.mp4", 711 "bbb_cif_768kbps_30fps_vp8_stereo_48kHz_192kbps_vorbis.mkv", 712 "bbb_cif_768kbps_30fps_vp8_stereo_48kHz_192kbps_vorbis.webm", 713 "bbb_stereo_48kHz_192kbps_vorbis.ogg",}}, 714 {MediaFormat.MIMETYPE_AUDIO_OPUS, new String[]{ 715 "bbb_cif_768kbps_30fps_vp9_stereo_48kHz_192kbps_opus.webm", 716 "bbb_cif_768kbps_30fps_vp9_stereo_48kHz_192kbps_opus.mkv", 717 "bbb_cif_768kbps_30fps_hevc_stereo_48kHz_192kbps_opus.mp4", 718 "bbb_stereo_48kHz_192kbps_opus.ogg",}}, 719 {MediaFormat.MIMETYPE_AUDIO_MPEG, new String[]{ 720 "bbb_stereo_48kHz_192kbps_mp3.mp3", 721 "bbb_cif_768kbps_30fps_mpeg2_stereo_48kHz_192kbps_mp3.mp4", 722 "bbb_cif_768kbps_30fps_hevc_stereo_48kHz_192kbps_mp3.mkv",}}, 723 {MediaFormat.MIMETYPE_AUDIO_AAC, new String[]{ 724 "bbb_stereo_48kHz_192kbps_aac.mp4", 725 "bbb_cif_768kbps_30fps_avc_stereo_48kHz_192kbps_aac.3gp", 726 "bbb_cif_768kbps_30fps_avc_stereo_48kHz_192kbps_aac.mkv", 727 "bbb_stereo_48kHz_128kbps_aac.ts",}}, 728 {MediaFormat.MIMETYPE_AUDIO_AMR_NB, new String[]{ 729 "bbb_cif_768kbps_30fps_h263_mono_8kHz_12kbps_amrnb.3gp", 730 "bbb_mono_8kHz_12kbps_amrnb.amr",}}, 731 {MediaFormat.MIMETYPE_AUDIO_AMR_WB, new String[]{ 732 "bbb_cif_768kbps_30fps_mpeg4_mono_16kHz_20kbps_amrwb.3gp", 733 "bbb_mono_16kHz_20kbps_amrwb.amr"}}, 734 {MediaFormat.MIMETYPE_AUDIO_FLAC, new String[]{ 735 "bbb_cif_768kbps_30fps_mpeg4_stereo_48kHz_192kbps_flac.mp4", 736 "bbb_cif_768kbps_30fps_h263_stereo_48kHz_192kbps_flac.mkv",}}, 737 {MediaFormat.MIMETYPE_AUDIO_RAW, new String[]{"canon.mid",}}, 738 {MediaFormat.MIMETYPE_AUDIO_AC3, new String[]{ 739 "testac3mp4.mp4", "testac3ts.ts",}}, 740 {MediaFormat.MIMETYPE_AUDIO_AC4, new String[]{"multi0.mp4",}}, 741 {MediaFormat.MIMETYPE_AUDIO_EAC3, new String[]{ 742 "testeac3mp4.mp4", "testeac3ts.ts",}}, 743 {MediaFormat.MIMETYPE_AUDIO_RAW, new String[]{"bbb_1ch_16kHz.wav",}}, 744 {MediaFormat.MIMETYPE_AUDIO_G711_ALAW, new String[]{"bbb_2ch_8kHz_alaw.wav",}}, 745 {MediaFormat.MIMETYPE_AUDIO_G711_MLAW, new String[]{"bbb_2ch_8kHz_mulaw.wav",}}, 746 {MediaFormat.MIMETYPE_AUDIO_MSGSM, new String[]{"bbb_1ch_8kHz_gsm.wav",}}, 747 }); 748 } 749 750 private native boolean nativeTestExtract(String srcPath, String refPath, String mediaType); 751 752 private native boolean nativeTestSeek(String srcPath, String mediaType); 753 754 private native boolean nativeTestSeekFlakiness(String srcPath, String mediaType); 755 756 private native boolean nativeTestSeekToZero(String srcPath, String mediaType); 757 758 private native boolean nativeTestFileFormat(String srcPath); 759 760 public FunctionalityTest(String mediaType, String[] srcFiles) { 761 mMediaType = mediaType; 762 mSrcFiles = srcFiles; 763 } 764 765 // content necessary for testing seek are grouped in this class 766 private class SeekTestParams { 767 MediaCodec.BufferInfo mExpected; 768 long mTimeStamp; 769 int mMode; 770 771 SeekTestParams(MediaCodec.BufferInfo expected, long timeStamp, int mode) { 772 mExpected = expected; 773 mTimeStamp = timeStamp; 774 mMode = mode; 775 } 776 } 777 778 private ArrayList<MediaCodec.BufferInfo> getSeekablePoints(String srcFile, String mediaType) 779 throws IOException { 780 ArrayList<MediaCodec.BufferInfo> bookmarks = null; 781 if (mediaType == null) return null; 782 MediaExtractor extractor = new MediaExtractor(); 783 Preconditions.assertTestFileExists(MEDIA_DIR + srcFile); 784 extractor.setDataSource(MEDIA_DIR + srcFile); 785 for (int trackID = 0; trackID < extractor.getTrackCount(); trackID++) { 786 MediaFormat format = extractor.getTrackFormat(trackID); 787 if (!mediaType.equals(format.getString(MediaFormat.KEY_MIME))) continue; 788 extractor.selectTrack(trackID); 789 bookmarks = new ArrayList<>(); 790 do { 791 int sampleFlags = extractor.getSampleFlags(); 792 if ((sampleFlags & MediaExtractor.SAMPLE_FLAG_SYNC) != 0) { 793 MediaCodec.BufferInfo sampleInfo = new MediaCodec.BufferInfo(); 794 sampleInfo.set(0, (int) extractor.getSampleSize(), 795 extractor.getSampleTime(), extractor.getSampleFlags()); 796 bookmarks.add(sampleInfo); 797 } 798 } while (extractor.advance()); 799 extractor.unselectTrack(trackID); 800 break; 801 } 802 extractor.release(); 803 return bookmarks; 804 } 805 806 private ArrayList<SeekTestParams> generateSeekTestArgs(String srcFile, String mediaType, 807 boolean isRandom) throws IOException { 808 ArrayList<SeekTestParams> testArgs = new ArrayList<>(); 809 if (mediaType == null) return null; 810 Preconditions.assertTestFileExists(MEDIA_DIR + srcFile); 811 if (isRandom) { 812 MediaExtractor extractor = new MediaExtractor(); 813 extractor.setDataSource(MEDIA_DIR + srcFile); 814 final long maxEstDuration = 4000000; 815 for (int trackID = 0; trackID < extractor.getTrackCount(); trackID++) { 816 MediaFormat format = extractor.getTrackFormat(trackID); 817 if (!mediaType.equals(format.getString(MediaFormat.KEY_MIME))) continue; 818 extractor.selectTrack(trackID); 819 for (int i = 0; i < MAX_SEEK_POINTS; i++) { 820 long pts = (long) (mRandNum.nextDouble() * maxEstDuration); 821 for (int mode = MediaExtractor.SEEK_TO_PREVIOUS_SYNC; 822 mode <= MediaExtractor.SEEK_TO_CLOSEST_SYNC; mode++) { 823 MediaCodec.BufferInfo currInfo = new MediaCodec.BufferInfo(); 824 extractor.seekTo(pts, mode); 825 currInfo.set(0, (int) extractor.getSampleSize(), 826 extractor.getSampleTime(), extractor.getSampleFlags()); 827 testArgs.add(new SeekTestParams(currInfo, pts, mode)); 828 } 829 } 830 extractor.unselectTrack(trackID); 831 break; 832 } 833 extractor.release(); 834 } else { 835 ArrayList<MediaCodec.BufferInfo> bookmarks = getSeekablePoints(srcFile, mediaType); 836 if (bookmarks == null) return null; 837 int size = bookmarks.size(); 838 int[] indices; 839 if (size > MAX_SEEK_POINTS) { 840 indices = new int[MAX_SEEK_POINTS]; 841 indices[0] = 0; 842 indices[MAX_SEEK_POINTS - 1] = size - 1; 843 for (int i = 1; i < MAX_SEEK_POINTS - 1; i++) { 844 indices[i] = (int) (mRandNum.nextDouble() * (MAX_SEEK_POINTS - 1) + 1); 845 } 846 } else { 847 indices = new int[size]; 848 for (int i = 0; i < size; i++) indices[i] = i; 849 } 850 // closest sync : Seek to the sync sample CLOSEST to the specified time 851 // previous sync : Seek to a sync sample AT or AFTER the specified time 852 // next sync : Seek to a sync sample AT or BEFORE the specified time 853 for (int i : indices) { 854 MediaCodec.BufferInfo currInfo = bookmarks.get(i); 855 long pts = currInfo.presentationTimeUs; 856 testArgs.add( 857 new SeekTestParams(currInfo, pts, MediaExtractor.SEEK_TO_CLOSEST_SYNC)); 858 testArgs.add( 859 new SeekTestParams(currInfo, pts, MediaExtractor.SEEK_TO_NEXT_SYNC)); 860 testArgs.add( 861 new SeekTestParams(currInfo, pts, 862 MediaExtractor.SEEK_TO_PREVIOUS_SYNC)); 863 if (i > 0) { 864 MediaCodec.BufferInfo prevInfo = bookmarks.get(i - 1); 865 long ptsMinus = prevInfo.presentationTimeUs; 866 ptsMinus = pts - ((pts - ptsMinus) >> 3); 867 testArgs.add(new SeekTestParams(currInfo, ptsMinus, 868 MediaExtractor.SEEK_TO_CLOSEST_SYNC)); 869 testArgs.add(new SeekTestParams(currInfo, ptsMinus, 870 MediaExtractor.SEEK_TO_NEXT_SYNC)); 871 testArgs.add(new SeekTestParams(prevInfo, ptsMinus, 872 MediaExtractor.SEEK_TO_PREVIOUS_SYNC)); 873 } 874 if (i < size - 1) { 875 MediaCodec.BufferInfo nextInfo = bookmarks.get(i + 1); 876 long ptsPlus = nextInfo.presentationTimeUs; 877 ptsPlus = pts + ((ptsPlus - pts) >> 3); 878 testArgs.add(new SeekTestParams(currInfo, ptsPlus, 879 MediaExtractor.SEEK_TO_CLOSEST_SYNC)); 880 testArgs.add(new SeekTestParams(nextInfo, ptsPlus, 881 MediaExtractor.SEEK_TO_NEXT_SYNC)); 882 testArgs.add(new SeekTestParams(currInfo, ptsPlus, 883 MediaExtractor.SEEK_TO_PREVIOUS_SYNC)); 884 } 885 } 886 } 887 return testArgs; 888 } 889 890 int checkSeekPoints(String srcFile, String mediaType, 891 ArrayList<SeekTestParams> seekTestArgs) throws IOException { 892 int errCnt = 0; 893 Preconditions.assertTestFileExists(MEDIA_DIR + srcFile); 894 MediaExtractor extractor = new MediaExtractor(); 895 extractor.setDataSource(MEDIA_DIR + srcFile); 896 for (int trackID = 0; trackID < extractor.getTrackCount(); trackID++) { 897 MediaFormat format = extractor.getTrackFormat(trackID); 898 if (!format.getString(MediaFormat.KEY_MIME).equals(mediaType)) continue; 899 extractor.selectTrack(trackID); 900 MediaCodec.BufferInfo received = new MediaCodec.BufferInfo(); 901 for (SeekTestParams arg : seekTestArgs) { 902 extractor.seekTo(arg.mTimeStamp, arg.mMode); 903 received.set(0, (int) extractor.getSampleSize(), extractor.getSampleTime(), 904 extractor.getSampleFlags()); 905 if (!isSampleInfoIdentical(arg.mExpected, received)) { 906 errCnt++; 907 if (ENABLE_LOGS) { 908 Log.d(LOG_TAG, " flags exp/got: " + arg.mExpected.flags + '/' + 909 received.flags); 910 Log.d(LOG_TAG, 911 " size exp/got: " + arg.mExpected.size + '/' + received.size); 912 Log.d(LOG_TAG, 913 " ts exp/got: " + arg.mExpected.presentationTimeUs + '/' + 914 received.presentationTimeUs); 915 } 916 } 917 } 918 extractor.unselectTrack(trackID); 919 break; 920 } 921 extractor.release(); 922 return errCnt; 923 } 924 925 private boolean isFileSeekable(String srcFile) throws IOException { 926 MediaExtractor ext = new MediaExtractor(); 927 Preconditions.assertTestFileExists(MEDIA_DIR + srcFile); 928 ext.setDataSource(MEDIA_DIR + srcFile); 929 String format = ext.getMetrics().getString(MediaExtractor.MetricsConstants.FORMAT); 930 ext.release(); 931 // MPEG2TS and MPEG2PS files are non-seekable 932 return !(format.equalsIgnoreCase("MPEG2TSExtractor") || 933 format.equalsIgnoreCase("MPEG2PSExtractor")); 934 } 935 936 /** 937 * Audio, Video codecs support a variety of file-types/container formats. For example, 938 * Vorbis supports OGG, MP4, WEBM and MKV. H.263 supports 3GPP, WEBM and MKV. For every 939 * mediaType, a list of test vectors are provided one for each container) but underlying 940 * elementary stream is the same for all. The streams of a mediaType are extracted and 941 * compared with each other for similarity. 942 */ 943 @CddTest(requirements = {"5.1.3", "5.1.8"}) 944 @LargeTest 945 @Test 946 public void testExtract() throws IOException { 947 assumeTrue(shouldRunTest(mMediaType)); 948 Preconditions.assertTestFileExists(MEDIA_DIR + mSrcFiles[0]); 949 MediaExtractor refExtractor = new MediaExtractor(); 950 refExtractor.setDataSource(MEDIA_DIR + mSrcFiles[0]); 951 long sdkChecksum = readAllData(refExtractor, mMediaType, Integer.MAX_VALUE); 952 long ndkChecksum = nativeReadAllData(MEDIA_DIR + mSrcFiles[0], mMediaType, 953 Integer.MAX_VALUE, null, null, false); 954 assertEquals("SDK and NDK checksums mismatch", sdkChecksum, ndkChecksum); 955 if (mSrcFiles.length == 1) { 956 refExtractor.release(); 957 return; 958 } 959 assumeTrue("TODO(b/146925481)", !mMediaType.equals(MediaFormat.MIMETYPE_AUDIO_VORBIS)); 960 assumeTrue("TODO(b/146925481)", !mMediaType.equals(MediaFormat.MIMETYPE_AUDIO_OPUS)); 961 assumeTrue("TODO(b/146925481)", !mMediaType.equals(MediaFormat.MIMETYPE_AUDIO_MPEG)); 962 assumeTrue("TODO(b/146925481)", !mMediaType.equals(MediaFormat.MIMETYPE_AUDIO_AAC)); 963 boolean isOk = true; 964 for (int i = 1; i < mSrcFiles.length && isOk; i++) { 965 MediaExtractor testExtractor = new MediaExtractor(); 966 Preconditions.assertTestFileExists(MEDIA_DIR + mSrcFiles[i]); 967 testExtractor.setDataSource(MEDIA_DIR + mSrcFiles[i]); 968 if (!isMediaSimilar(refExtractor, testExtractor, mMediaType, Integer.MAX_VALUE)) { 969 if (ENABLE_LOGS) { 970 Log.d(LOG_TAG, "Files: " + mSrcFiles[0] + ", " + mSrcFiles[i] + 971 " are different from extractor perspective"); 972 } 973 if (!codecListSupp.contains(mMediaType)) { 974 isOk = false; 975 } 976 } 977 testExtractor.release(); 978 } 979 refExtractor.release(); 980 assertTrue(testName.getMethodName() + " failed for Mediatype: " + mMediaType, isOk); 981 } 982 983 /** 984 * Tests seek functionality, verifies if we seek to most accurate point for a given 985 * choice of timestamp and mode. 986 */ 987 @ApiTest(apis = {"android.media.MediaExtractor#seekTo", 988 "android.media.MediaExtractor#SEEK_TO_CLOSEST_SYNC", 989 "android.media.MediaExtractor#SEEK_TO_NEXT_SYNC", 990 "android.media.MediaExtractor#SEEK_TO_PREVIOUS_SYNC"}) 991 @LargeTest 992 @Test 993 @Ignore("TODO(b/146420831)") 994 public void testSeek() throws IOException { 995 assumeTrue(shouldRunTest(mMediaType) 996 && !mMediaType.equals(MediaFormat.MIMETYPE_AUDIO_RAW)); 997 boolean isOk = true; 998 for (String srcFile : mSrcFiles) { 999 if (!isFileSeekable(srcFile)) continue; 1000 ArrayList<SeekTestParams> seekTestArgs = 1001 generateSeekTestArgs(srcFile, mMediaType, false); 1002 assertTrue("mediaType is null.", seekTestArgs != null); 1003 assertTrue("No sync samples found.", !seekTestArgs.isEmpty()); 1004 Collections.shuffle(seekTestArgs, mRandNum); 1005 int seekAccErrCnt = checkSeekPoints(srcFile, mMediaType, seekTestArgs); 1006 if (seekAccErrCnt != 0) { 1007 if (ENABLE_LOGS) { 1008 Log.d(LOG_TAG, "For " + srcFile + " seek chose inaccurate Sync point in: " + 1009 seekAccErrCnt + "/" + seekTestArgs.size()); 1010 } 1011 if (!codecListSupp.contains(mMediaType)) { 1012 isOk = false; 1013 break; 1014 } 1015 } 1016 } 1017 assertTrue(testName.getMethodName() + " failed for mediaType: " + mMediaType, isOk); 1018 } 1019 1020 /** 1021 * Tests if we get the same content each time after a call to seekto; 1022 */ 1023 @ApiTest(apis = {"android.media.MediaExtractor#seekTo", 1024 "android.media.MediaExtractor#SEEK_TO_CLOSEST_SYNC", 1025 "android.media.MediaExtractor#SEEK_TO_NEXT_SYNC", 1026 "android.media.MediaExtractor#SEEK_TO_PREVIOUS_SYNC"}) 1027 @LargeTest 1028 @Test 1029 public void testSeekFlakiness() throws IOException { 1030 assumeTrue(shouldRunTest(mMediaType)); 1031 boolean isOk = true; 1032 for (String srcFile : mSrcFiles) { 1033 if (!isFileSeekable(srcFile)) continue; 1034 ArrayList<SeekTestParams> seekTestArgs = 1035 generateSeekTestArgs(srcFile, mMediaType, true); 1036 assertTrue("mediaType is null.", seekTestArgs != null); 1037 assertTrue("No samples found.", !seekTestArgs.isEmpty()); 1038 Collections.shuffle(seekTestArgs, mRandNum); 1039 int flakyErrCnt = checkSeekPoints(srcFile, mMediaType, seekTestArgs); 1040 if (flakyErrCnt != 0) { 1041 if (ENABLE_LOGS) { 1042 Log.d(LOG_TAG, 1043 "No. of Samples where seek showed flakiness is: " + flakyErrCnt); 1044 } 1045 if (!codecListSupp.contains(mMediaType)) { 1046 isOk = false; 1047 break; 1048 } 1049 } 1050 } 1051 assertTrue(testName.getMethodName() + " failed for Mediatype: " + mMediaType, isOk); 1052 } 1053 1054 /** 1055 * Test if seekTo(0) yields the same content as if we had just opened the file and started 1056 * reading. 1057 */ 1058 @ApiTest(apis = {"android.media.MediaExtractor#seekTo", 1059 "android.media.MediaExtractor#SEEK_TO_CLOSEST_SYNC", 1060 "android.media.MediaExtractor#SEEK_TO_NEXT_SYNC", 1061 "android.media.MediaExtractor#SEEK_TO_PREVIOUS_SYNC"}) 1062 @SmallTest 1063 @Test 1064 public void testSeekToZero() throws IOException { 1065 assumeTrue(shouldRunTest(mMediaType)); 1066 assumeTrue("TODO(b/146925481)", !mMediaType.equals(MediaFormat.MIMETYPE_AUDIO_VORBIS)); 1067 assumeTrue("TODO(b/146925481)", !mMediaType.equals(MediaFormat.MIMETYPE_AUDIO_MPEG)); 1068 assumeTrue("TODO(b/146925481)", !mMediaType.equals(MediaFormat.MIMETYPE_AUDIO_AAC)); 1069 boolean isOk = true; 1070 for (String srcFile : mSrcFiles) { 1071 if (!isFileSeekable(srcFile)) continue; 1072 MediaExtractor extractor = new MediaExtractor(); 1073 Preconditions.assertTestFileExists(MEDIA_DIR + srcFile); 1074 extractor.setDataSource(MEDIA_DIR + srcFile); 1075 MediaCodec.BufferInfo sampleInfoAtZero = new MediaCodec.BufferInfo(); 1076 MediaCodec.BufferInfo currInfo = new MediaCodec.BufferInfo(); 1077 final long randomSeekPts = 1 << 20; 1078 for (int trackID = 0; trackID < extractor.getTrackCount(); trackID++) { 1079 MediaFormat format = extractor.getTrackFormat(trackID); 1080 if (!mMediaType.equals(format.getString(MediaFormat.KEY_MIME))) continue; 1081 extractor.selectTrack(trackID); 1082 sampleInfoAtZero.set(0, (int) extractor.getSampleSize(), 1083 extractor.getSampleTime(), extractor.getSampleFlags()); 1084 extractor.seekTo(randomSeekPts, MediaExtractor.SEEK_TO_NEXT_SYNC); 1085 extractor.seekTo(0, MediaExtractor.SEEK_TO_CLOSEST_SYNC); 1086 currInfo.set(0, (int) extractor.getSampleSize(), 1087 extractor.getSampleTime(), extractor.getSampleFlags()); 1088 if (!isSampleInfoIdentical(sampleInfoAtZero, currInfo)) { 1089 if (!codecListSupp.contains(mMediaType)) { 1090 if (ENABLE_LOGS) { 1091 Log.d(LOG_TAG, "seen mismatch seekTo(0, SEEK_TO_CLOSEST_SYNC)"); 1092 Log.d(LOG_TAG, " flags exp/got: " + sampleInfoAtZero.flags + '/' + 1093 currInfo.flags); 1094 Log.d(LOG_TAG, " size exp/got: " + sampleInfoAtZero.size + '/' + 1095 currInfo.size); 1096 Log.d(LOG_TAG, 1097 " ts exp/got: " + sampleInfoAtZero.presentationTimeUs + 1098 '/' + currInfo.presentationTimeUs); 1099 } 1100 isOk = false; 1101 break; 1102 } 1103 } 1104 extractor.seekTo(-1L, MediaExtractor.SEEK_TO_CLOSEST_SYNC); 1105 currInfo.set(0, (int) extractor.getSampleSize(), 1106 extractor.getSampleTime(), extractor.getSampleFlags()); 1107 if (!isSampleInfoIdentical(sampleInfoAtZero, currInfo)) { 1108 if (!codecListSupp.contains(mMediaType)) { 1109 if (ENABLE_LOGS) { 1110 Log.d(LOG_TAG, "seen mismatch seekTo(-1, SEEK_TO_CLOSEST_SYNC)"); 1111 Log.d(LOG_TAG, " flags exp/got: " + sampleInfoAtZero.flags + '/' + 1112 currInfo.flags); 1113 Log.d(LOG_TAG, " size exp/got: " + sampleInfoAtZero.size + '/' + 1114 currInfo.size); 1115 Log.d(LOG_TAG, 1116 " ts exp/got: " + sampleInfoAtZero.presentationTimeUs + 1117 '/' + currInfo.presentationTimeUs); 1118 } 1119 isOk = false; 1120 break; 1121 } 1122 } 1123 extractor.unselectTrack(trackID); 1124 } 1125 extractor.release(); 1126 } 1127 assertTrue(testName.getMethodName() + " failed for Mediatype: " + mMediaType, isOk); 1128 } 1129 1130 @ApiTest(apis = {"android.media.MediaExtractor#getMetrics"}) 1131 @SmallTest 1132 @Test 1133 public void testMetrics() throws IOException { 1134 assumeTrue(shouldRunTest(mMediaType)); 1135 for (String srcFile : mSrcFiles) { 1136 MediaExtractor extractor = new MediaExtractor(); 1137 Preconditions.assertTestFileExists(MEDIA_DIR + srcFile); 1138 extractor.setDataSource(MEDIA_DIR + srcFile); 1139 PersistableBundle bundle = extractor.getMetrics(); 1140 int numTracks = bundle.getInt(MediaExtractor.MetricsConstants.TRACKS); 1141 String format = bundle.getString(MediaExtractor.MetricsConstants.FORMAT); 1142 String mediaType = bundle.getString(MediaExtractor.MetricsConstants.MIME_TYPE); 1143 assertTrue(numTracks == extractor.getTrackCount() && format != null && 1144 mediaType != null); 1145 extractor.release(); 1146 } 1147 } 1148 1149 @CddTest(requirements = {"5.1.3", "5.1.8"}) 1150 @LargeTest 1151 @Test 1152 public void testExtractNative() { 1153 assumeTrue(shouldRunTest(mMediaType)); 1154 if (mSrcFiles.length == 1) return; 1155 assumeTrue("TODO(b/146925481)", !mMediaType.equals(MediaFormat.MIMETYPE_AUDIO_VORBIS)); 1156 assumeTrue("TODO(b/146925481)", !mMediaType.equals(MediaFormat.MIMETYPE_AUDIO_OPUS)); 1157 assumeTrue("TODO(b/146925481)", !mMediaType.equals(MediaFormat.MIMETYPE_AUDIO_MPEG)); 1158 assumeTrue("TODO(b/146925481)", !mMediaType.equals(MediaFormat.MIMETYPE_AUDIO_AAC)); 1159 boolean isOk = true; 1160 Preconditions.assertTestFileExists(MEDIA_DIR + mSrcFiles[0]); 1161 for (int i = 1; i < mSrcFiles.length; i++) { 1162 Preconditions.assertTestFileExists(MEDIA_DIR + mSrcFiles[i]); 1163 if (!nativeTestExtract(MEDIA_DIR + mSrcFiles[0], MEDIA_DIR + mSrcFiles[i], 1164 mMediaType)) { 1165 Log.d(LOG_TAG, "Files: " + mSrcFiles[0] + ", " + mSrcFiles[i] + 1166 " are different from extractor perpsective"); 1167 if (!codecListSupp.contains(mMediaType)) { 1168 isOk = false; 1169 break; 1170 } 1171 } 1172 } 1173 assertTrue(testName.getMethodName() + " failed for Mediatype: " + mMediaType, isOk); 1174 } 1175 1176 @ApiTest(apis = {"AMediaExtractor_seekTo", "AMEDIAEXTRACTOR_SEEK_CLOSEST_SYNC", 1177 "AMEDIAEXTRACTOR_SEEK_NEXT_SYNC", "AMEDIAEXTRACTOR_SEEK_PREVIOUS_SYNC"}) 1178 @LargeTest 1179 @Test 1180 @Ignore("TODO(b/146420831)") 1181 public void testSeekNative() throws IOException { 1182 assumeTrue(shouldRunTest(mMediaType) 1183 && !mMediaType.equals(MediaFormat.MIMETYPE_AUDIO_RAW)); 1184 boolean isOk = true; 1185 for (String srcFile : mSrcFiles) { 1186 Preconditions.assertTestFileExists(MEDIA_DIR + srcFile); 1187 if (!isFileSeekable(srcFile)) continue; 1188 if (!nativeTestSeek(MEDIA_DIR + srcFile, mMediaType)) { 1189 if (!codecListSupp.contains(mMediaType)) { 1190 isOk = false; 1191 break; 1192 } 1193 } 1194 } 1195 assertTrue(testName.getMethodName() + " failed for Mediatype: " + mMediaType, isOk); 1196 } 1197 1198 @ApiTest(apis = {"AMediaExtractor_seekTo", "AMEDIAEXTRACTOR_SEEK_CLOSEST_SYNC", 1199 "AMEDIAEXTRACTOR_SEEK_NEXT_SYNC", "AMEDIAEXTRACTOR_SEEK_PREVIOUS_SYNC"}) 1200 @LargeTest 1201 @Test 1202 public void testSeekFlakinessNative() throws IOException { 1203 assumeTrue(shouldRunTest(mMediaType)); 1204 boolean isOk = true; 1205 for (String srcFile : mSrcFiles) { 1206 Preconditions.assertTestFileExists(MEDIA_DIR + srcFile); 1207 if (!isFileSeekable(srcFile)) continue; 1208 if (!nativeTestSeekFlakiness(MEDIA_DIR + srcFile, mMediaType)) { 1209 if (!codecListSupp.contains(mMediaType)) { 1210 isOk = false; 1211 break; 1212 } 1213 } 1214 } 1215 assertTrue(testName.getMethodName() + " failed for Mediatype: " + mMediaType, isOk); 1216 } 1217 1218 @ApiTest(apis = {"AMediaExtractor_seekTo", "AMEDIAEXTRACTOR_SEEK_CLOSEST_SYNC", 1219 "AMEDIAEXTRACTOR_SEEK_NEXT_SYNC", "AMEDIAEXTRACTOR_SEEK_PREVIOUS_SYNC"}) 1220 @SmallTest 1221 @Test 1222 public void testSeekToZeroNative() throws IOException { 1223 assumeTrue(shouldRunTest(mMediaType)); 1224 assumeTrue("TODO(b/146925481)", !mMediaType.equals(MediaFormat.MIMETYPE_AUDIO_VORBIS)); 1225 assumeTrue("TODO(b/146925481)", !mMediaType.equals(MediaFormat.MIMETYPE_AUDIO_MPEG)); 1226 assumeTrue("TODO(b/146925481)", !mMediaType.equals(MediaFormat.MIMETYPE_AUDIO_AAC)); 1227 boolean isOk = true; 1228 for (String srcFile : mSrcFiles) { 1229 Preconditions.assertTestFileExists(MEDIA_DIR + srcFile); 1230 if (!isFileSeekable(srcFile)) continue; 1231 if (!nativeTestSeekToZero(MEDIA_DIR + srcFile, mMediaType)) { 1232 if (!codecListSupp.contains(mMediaType)) { 1233 isOk = false; 1234 break; 1235 } 1236 } 1237 } 1238 assertTrue(testName.getMethodName() + " failed for Mediatype: " + mMediaType, isOk); 1239 } 1240 1241 @ApiTest(apis = "AMediaExtractor_getFileFormat") 1242 @SmallTest 1243 @Test 1244 public void testFileFormatNative() { 1245 assumeTrue(shouldRunTest(mMediaType)); 1246 boolean isOk = true; 1247 for (String srcFile : mSrcFiles) { 1248 Preconditions.assertTestFileExists(MEDIA_DIR + srcFile); 1249 if (!nativeTestFileFormat(MEDIA_DIR + srcFile)) { 1250 isOk = false; 1251 break; 1252 } 1253 } 1254 assertTrue(testName.getMethodName() + " failed for Mediatype: " + mMediaType, isOk); 1255 } 1256 } 1257 1258 /** 1259 * Encloses extractor test for validating extractor output for extractors which directly 1260 * decode instead of extracting. 1261 */ 1262 @RunWith(Parameterized.class) 1263 public static class FusedExtractorDecoderTest { 1264 private final String mMediaType; 1265 private final String mRefFile; 1266 private final String mTestFile; 1267 1268 public FusedExtractorDecoderTest(String mediaType, String refFile, String testFile) { 1269 mMediaType = mediaType; 1270 mRefFile = refFile; 1271 mTestFile = testFile; 1272 } 1273 1274 @Parameterized.Parameters(name = "{index}_{0}") 1275 public static Collection<Object[]> input() { 1276 return Arrays.asList(new Object[][]{ 1277 {MediaFormat.MIMETYPE_AUDIO_FLAC, 1278 "bbb_cif_768kbps_30fps_mpeg4_stereo_48kHz_192kbps_flac.mp4", 1279 "bbb_stereo_48kHz_192kbps_flac.flac"}, 1280 /* TODO(b/163566531) 1281 {MediaFormat.MIMETYPE_AUDIO_RAW, "bbb_1ch_16kHz.mkv", "bbb_1ch_16kHz.wav"},*/ 1282 }); 1283 } 1284 1285 @CddTest(requirements = {"5.1.3"}) 1286 @LargeTest 1287 @Test 1288 public void testExtractDecodeAndValidate() throws IOException, InterruptedException { 1289 MediaExtractor testExtractor = new MediaExtractor(); 1290 testExtractor.setDataSource(MEDIA_DIR + mTestFile); 1291 MediaFormat format = testExtractor.getTrackFormat(0); 1292 String mediaType = format.getString(MediaFormat.KEY_MIME); 1293 if (mediaType.equals(MediaFormat.MIMETYPE_AUDIO_RAW)) { 1294 ArrayList<String> listOfDecoders = 1295 CodecTestBase.selectCodecs(mMediaType, null, null, false); 1296 assertTrue("no suitable codecs found for Mediatype: " + mMediaType, 1297 !listOfDecoders.isEmpty()); 1298 CodecDecoderTestBase cdtb = 1299 new CodecDecoderTestBase(listOfDecoders.get(0), mMediaType, mRefFile, 1300 "invalid"); 1301 cdtb.decodeToMemory(MEDIA_DIR + mRefFile, listOfDecoders.get(0), 0, 1302 MediaExtractor.SEEK_TO_CLOSEST_SYNC, Integer.MAX_VALUE); 1303 String log = String.format("test file: %s, ref file: %s:: ", mTestFile, mRefFile); 1304 final ByteBuffer refBuffer = cdtb.getOutputManager().getBuffer(); 1305 1306 testExtractor.selectTrack(0); 1307 ByteBuffer testBuffer = ByteBuffer.allocate(refBuffer.limit()); 1308 int bufOffset = 0; 1309 while (true) { 1310 long bytesRead = testExtractor.readSampleData(testBuffer, bufOffset); 1311 if (bytesRead == -1) break; 1312 bufOffset += bytesRead; 1313 testExtractor.advance(); 1314 } 1315 testBuffer.rewind(); 1316 assertEquals(log + "Output mismatch", 0, refBuffer.compareTo(testBuffer)); 1317 assertTrue(log + "Output formats mismatch", 1318 cdtb.isFormatSimilar(cdtb.getOutputFormat(), format)); 1319 } else if (mediaType.equals(mMediaType)) { 1320 MediaExtractor refExtractor = new MediaExtractor(); 1321 refExtractor.setDataSource(MEDIA_DIR + mRefFile); 1322 if (!isMediaSimilar(refExtractor, testExtractor, mMediaType, Integer.MAX_VALUE)) { 1323 fail("Files: " + mRefFile + ", " + mTestFile + 1324 " are different from extractor perspective"); 1325 } 1326 refExtractor.release(); 1327 } else { 1328 fail("unexpected Mediatype: " + mediaType); 1329 } 1330 testExtractor.release(); 1331 } 1332 } 1333 1334 /** 1335 * Test if extractor populates key-value pairs correctly 1336 */ 1337 @RunWith(Parameterized.class) 1338 public static class ValidateKeyValuePairs { 1339 private static final String MEDIA_DIR = WorkDir.getMediaDirString(); 1340 private final String mMediaType; 1341 private final String[] mInpFiles; 1342 private final int mProfile; 1343 private final int mLevel; 1344 private final int mWR; 1345 private final int mHCh; 1346 1347 public ValidateKeyValuePairs(String mediaType, String[] inpFiles, int profile, int level, 1348 int wr, int hCh) { 1349 mMediaType = mediaType; 1350 mInpFiles = inpFiles; 1351 mProfile = profile; 1352 mLevel = level; 1353 mWR = wr; 1354 mHCh = hCh; 1355 } 1356 1357 @Parameterized.Parameters(name = "{index}_{0}") 1358 public static Collection<Object[]> input() { 1359 // mediaType, clips, profile, level, width/sample rate, height/channel count 1360 List<Object[]> exhaustiveArgsList = new ArrayList<>(); 1361 1362 if (hasDecoder(MediaFormat.MIMETYPE_VIDEO_MPEG2)) { 1363 // profile and level constraints as per sec 2.3.2 of cdd 1364 /* TODO(b/159582475) 1365 exhaustiveArgsList.add(new Object[]{MediaFormat.MIMETYPE_VIDEO_MPEG2, new String[]{ 1366 "bbb_1920x1080_30fps_mpeg2_main_high.mp4", 1367 "bbb_1920x1080_30fps_mpeg2_main_high.mkv"}, 1368 MediaCodecInfo.CodecProfileLevel.MPEG2ProfileMain, 1369 MediaCodecInfo.CodecProfileLevel.MPEG2LevelHL, 1920, 1080});*/ 1370 } 1371 1372 if (hasDecoder(MediaFormat.MIMETYPE_VIDEO_AVC)) { 1373 // profile and level constraints as per sec 2.3.2 of cdd 1374 exhaustiveArgsList.add(new Object[]{MediaFormat.MIMETYPE_VIDEO_AVC, new String[]{ 1375 "bbb_1920x1080_avc_baseline_l42.mp4", 1376 "bbb_1920x1080_avc_baseline_l42.mkv", 1377 "bbb_1920x1080_avc_baseline_l42.3gp"}, 1378 MediaCodecInfo.CodecProfileLevel.AVCProfileConstrainedBaseline, 1379 MediaCodecInfo.CodecProfileLevel.AVCLevel42, 1920, 1080}); 1380 exhaustiveArgsList.add(new Object[]{MediaFormat.MIMETYPE_VIDEO_AVC, new String[]{ 1381 "bbb_1920x1080_avc_main_l42.mp4", 1382 "bbb_1920x1080_avc_main_l42.mkv", 1383 "bbb_1920x1080_avc_main_l42.3gp"}, 1384 MediaCodecInfo.CodecProfileLevel.AVCProfileMain, 1385 MediaCodecInfo.CodecProfileLevel.AVCLevel42, 1920, 1080}); 1386 exhaustiveArgsList.add(new Object[]{MediaFormat.MIMETYPE_VIDEO_AVC, new String[]{ 1387 "bbb_1920x1080_avc_high_l42.mp4", 1388 "bbb_1920x1080_avc_high_l42.mkv", 1389 "bbb_1920x1080_avc_high_l42.3gp"}, 1390 MediaCodecInfo.CodecProfileLevel.AVCProfileHigh, 1391 MediaCodecInfo.CodecProfileLevel.AVCLevel42, 1920, 1080}); 1392 exhaustiveArgsList.add(new Object[]{MediaFormat.MIMETYPE_VIDEO_AVC, new String[]{ 1393 "video_dovi_1920x1080_60fps_dvav_09.mp4"}, 1394 MediaCodecInfo.CodecProfileLevel.AVCProfileHigh, 1395 MediaCodecInfo.CodecProfileLevel.AVCLevel42, 1920, 1080}); 1396 // profile/level constraints for avc as per sec 5.3.4 of cdd 1397 exhaustiveArgsList.add(new Object[]{MediaFormat.MIMETYPE_VIDEO_AVC, new String[]{ 1398 "bbb_1920x1080_avc_baseline_l40.mp4", 1399 "bbb_1920x1080_avc_baseline_l40.mkv", 1400 "bbb_1920x1080_avc_baseline_l40.3gp"}, 1401 MediaCodecInfo.CodecProfileLevel.AVCProfileConstrainedBaseline, 1402 MediaCodecInfo.CodecProfileLevel.AVCLevel4, 1920, 1080}); 1403 exhaustiveArgsList.add(new Object[]{MediaFormat.MIMETYPE_VIDEO_AVC, new String[]{ 1404 "bbb_1920x1080_avc_main_l40.mp4", 1405 "bbb_1920x1080_avc_main_l40.mkv", 1406 "bbb_1920x1080_avc_main_l40.3gp"}, 1407 MediaCodecInfo.CodecProfileLevel.AVCProfileMain, 1408 MediaCodecInfo.CodecProfileLevel.AVCLevel4, 1920, 1080}); 1409 } 1410 1411 if (hasDecoder(MediaFormat.MIMETYPE_VIDEO_HEVC)) { 1412 // profile and level constraints as per sec 2.3.2 of cdd 1413 exhaustiveArgsList.add(new Object[]{MediaFormat.MIMETYPE_VIDEO_HEVC, new String[]{ 1414 "bbb_1920x1080_hevc_main_l41.mp4", 1415 "bbb_1920x1080_hevc_main_l41.mkv"}, 1416 MediaCodecInfo.CodecProfileLevel.HEVCProfileMain, 1417 MediaCodecInfo.CodecProfileLevel.HEVCMainTierLevel41, 1920, 1080}); 1418 // profile/level constraints for hevc as per sec 5.3.5 of cdd 1419 exhaustiveArgsList.add(new Object[]{MediaFormat.MIMETYPE_VIDEO_HEVC, new String[]{ 1420 "bbb_1920x1080_hevc_main_l40.mp4", 1421 "bbb_1920x1080_hevc_main_l40.mkv"}, 1422 MediaCodecInfo.CodecProfileLevel.HEVCProfileMain, 1423 MediaCodecInfo.CodecProfileLevel.HEVCMainTierLevel4, 1920, 1080}); 1424 exhaustiveArgsList.add(new Object[]{MediaFormat.MIMETYPE_VIDEO_HEVC, new String[]{ 1425 "video_dovi_1920x1080_30fps_dvhe_04.mp4"}, 1426 MediaCodecInfo.CodecProfileLevel.HEVCProfileMain10, 1427 MediaCodecInfo.CodecProfileLevel.HEVCMainTierLevel4, 1920, 1080}); 1428 exhaustiveArgsList.add(new Object[]{MediaFormat.MIMETYPE_VIDEO_HEVC, new String[]{ 1429 "video_dovi_1920x1080_60fps_dvhe_08.mp4"}, 1430 MediaCodecInfo.CodecProfileLevel.HEVCProfileMain10, 1431 MediaCodecInfo.CodecProfileLevel.HEVCMainTierLevel41, 1920, 1080}); 1432 } 1433 1434 if (hasDecoder(MediaFormat.MIMETYPE_VIDEO_VP9)) { 1435 // profile and level constraints as per sec 2.3.2 of cdd 1436 exhaustiveArgsList.add(new Object[]{MediaFormat.MIMETYPE_VIDEO_VP9, new String[]{ 1437 "bbb_1920x1080_vp9_main_l41.webm", 1438 "bbb_1920x1080_vp9_main_l41.mkv"}, 1439 MediaCodecInfo.CodecProfileLevel.VP9Profile0, 1440 MediaCodecInfo.CodecProfileLevel.VP9Level41, 1920, 1080}); 1441 // profile/level constraints for vp9 as per sec 5.3.6 of cdd 1442 exhaustiveArgsList.add(new Object[]{MediaFormat.MIMETYPE_VIDEO_VP9, new String[]{ 1443 "bbb_1920x1080_vp9_main_l40.webm", 1444 "bbb_1920x1080_vp9_main_l40.mkv"}, 1445 MediaCodecInfo.CodecProfileLevel.VP9Profile0, 1446 MediaCodecInfo.CodecProfileLevel.VP9Level4, 1920, 1080}); 1447 exhaustiveArgsList.add(new Object[]{MediaFormat.MIMETYPE_VIDEO_VP9, new String[]{ 1448 "color_bands_176x176_vp9_10bit_fr.webm"}, 1449 MediaCodecInfo.CodecProfileLevel.VP9Profile2, 1450 MediaCodecInfo.CodecProfileLevel.VP9Level4, 176, 176}); 1451 } 1452 1453 if (hasDecoder(MediaFormat.MIMETYPE_VIDEO_H263)) { 1454 // profile/level constraints for h263 as per sec 5.3.2 of cdd 1455 /* TODO(b/159582475) 1456 exhaustiveArgsList.add(new Object[]{MediaFormat.MIMETYPE_VIDEO_H263, new String[]{ 1457 "bbb_352x288_384kbps_30fps_h263_baseline_l3.3gp", 1458 "bbb_352x288_384kbps_30fps_h263_baseline_l3.mp4", 1459 "bbb_352x288_384kbps_30fps_h263_baseline_l3.mkv"}, 1460 MediaCodecInfo.CodecProfileLevel.H263ProfileBaseline, 1461 MediaCodecInfo.CodecProfileLevel.H263Level30, 352, 288});*/ 1462 } 1463 1464 if (hasDecoder(MediaFormat.MIMETYPE_VIDEO_MPEG4)) { 1465 // profile/level constraints for mpeg4 as per sec 5.3.3 of cdd 1466 exhaustiveArgsList.add(new Object[]{MediaFormat.MIMETYPE_VIDEO_MPEG4, new String[]{ 1467 "bbb_352x288_384kbps_30fps_mpeg4_simple_l3.mp4", 1468 "bbb_352x288_384kbps_30fps_mpeg4_simple_l3.3gp", 1469 "bbb_352x288_384kbps_30fps_mpeg4_simple_l3.mkv"}, 1470 MediaCodecInfo.CodecProfileLevel.MPEG4ProfileSimple, 1471 MediaCodecInfo.CodecProfileLevel.MPEG4Level3, 352, 288}); 1472 } 1473 1474 if (hasDecoder(MediaFormat.MIMETYPE_AUDIO_AAC)) { 1475 // profile and level constraints for devices that have audio output as per sec 2.2.2, 1476 // sec 2.3.2, sec 2.5.2, sec 5.1.2 of cdd 1477 exhaustiveArgsList.add(new Object[]{MediaFormat.MIMETYPE_AUDIO_AAC, new String[]{ 1478 "bbb_stereo_44kHz_192kbps_aac_lc.mp4", 1479 "bbb_stereo_44kHz_192kbps_aac_lc.3gp", 1480 "bbb_stereo_44kHz_192kbps_aac_lc.mkv"}, 1481 MediaCodecInfo.CodecProfileLevel.AACObjectLC, 0, 44100, 2}); 1482 /* TODO(b/159582475) 1483 exhaustiveArgsList.add(new Object[]{MediaFormat.MIMETYPE_AUDIO_AAC, new String[]{ 1484 "bbb_stereo_44kHz_192kbps_aac_he.mp4", 1485 "bbb_stereo_44kHz_192kbps_aac_he.3gp", 1486 "bbb_stereo_44kHz_192kbps_aac_he.mkv"}, 1487 MediaCodecInfo.CodecProfileLevel.AACObjectHE, 0, 44100, 2});*/ 1488 exhaustiveArgsList.add(new Object[]{MediaFormat.MIMETYPE_AUDIO_AAC, new String[] { 1489 "bbb_stereo_44kHz_192kbps_aac_eld.mp4", 1490 "bbb_stereo_44kHz_192kbps_aac_eld.3gp", 1491 "bbb_stereo_44kHz_192kbps_aac_eld.mkv"}, 1492 MediaCodecInfo.CodecProfileLevel.AACObjectELD, 0, 44100, 2}); 1493 exhaustiveArgsList.add(new Object[]{MediaFormat.MIMETYPE_AUDIO_AAC, new String[]{ 1494 "bbb_stereo_44kHz_192kbps_aac_ld.mp4", 1495 "bbb_stereo_44kHz_192kbps_aac_ld.3gp", 1496 "bbb_stereo_44kHz_192kbps_aac_ld.mkv"}, 1497 MediaCodecInfo.CodecProfileLevel.AACObjectLD, 0, 44100, 2}); 1498 /*TODO(b/159582475) 1499 exhaustiveArgsList.add(new Object[]{MediaFormat.MIMETYPE_AUDIO_AAC, new String[]{ 1500 "bbb_stereo_44kHz_192kbps_aac_hev2.mp4", 1501 "bbb_stereo_44kHz_192kbps_aac_hev2.3gp", 1502 "bbb_stereo_44kHz_192kbps_aac_hev2.mkv"}, 1503 MediaCodecInfo.CodecProfileLevel.AACObjectHE_PS, 0, 44100, 2});*/ 1504 } 1505 1506 // Miscellaneous 1507 if (hasDecoder(MediaFormat.MIMETYPE_VIDEO_DOLBY_VISION)) { 1508 exhaustiveArgsList.add(new Object[]{MediaFormat.MIMETYPE_VIDEO_DOLBY_VISION, 1509 new String[]{"video_dovi_1920x1080_30fps_dvhe_04.mp4"}, 1510 MediaCodecInfo.CodecProfileLevel.DolbyVisionProfileDvheDtr, 1511 MediaCodecInfo.CodecProfileLevel.DolbyVisionLevelFhd30, 1920, 1080}); 1512 exhaustiveArgsList.add(new Object[]{MediaFormat.MIMETYPE_VIDEO_DOLBY_VISION, 1513 new String[]{"video_dovi_1920x1080_60fps_dvhe_05.mp4"}, 1514 MediaCodecInfo.CodecProfileLevel.DolbyVisionProfileDvheStn, 1515 MediaCodecInfo.CodecProfileLevel.DolbyVisionLevelFhd60, 1920, 1080}); 1516 exhaustiveArgsList.add(new Object[]{MediaFormat.MIMETYPE_VIDEO_DOLBY_VISION, 1517 new String[]{"video_dovi_1920x1080_60fps_dvhe_08.mp4"}, 1518 MediaCodecInfo.CodecProfileLevel.DolbyVisionProfileDvheSt, 1519 MediaCodecInfo.CodecProfileLevel.DolbyVisionLevelFhd60, 1920, 1080}); 1520 exhaustiveArgsList.add(new Object[]{MediaFormat.MIMETYPE_VIDEO_DOLBY_VISION, 1521 new String[]{"video_dovi_1920x1080_60fps_dvav_09.mp4"}, 1522 MediaCodecInfo.CodecProfileLevel.DolbyVisionProfileDvavSe, 1523 MediaCodecInfo.CodecProfileLevel.DolbyVisionLevelFhd60, 1920, 1080}); 1524 } 1525 1526 return exhaustiveArgsList; 1527 } 1528 1529 @ApiTest(apis = {"android.media.MediaExtractor#getTrackFormat"}) 1530 @Test 1531 public void validateKeyValuePairs() throws IOException { 1532 for (String file : mInpFiles) { 1533 MediaFormat format = null; 1534 MediaExtractor extractor = new MediaExtractor(); 1535 Preconditions.assertTestFileExists(MEDIA_DIR + file); 1536 extractor.setDataSource(MEDIA_DIR + file); 1537 for (int trackID = 0; trackID < extractor.getTrackCount(); trackID++) { 1538 MediaFormat fmt = extractor.getTrackFormat(trackID); 1539 if (mMediaType.equalsIgnoreCase(fmt.getString(MediaFormat.KEY_MIME))) { 1540 format = fmt; 1541 break; 1542 } 1543 } 1544 extractor.release(); 1545 assertTrue("missing track format from file " + file, format != null); 1546 if (mMediaType.equals(MediaFormat.MIMETYPE_AUDIO_AAC)) { 1547 assertTrue("neither KEY_AAC_PROFILE nor KEY_PROFILE found in file " + file, 1548 format.containsKey(MediaFormat.KEY_AAC_PROFILE) || 1549 format.containsKey(MediaFormat.KEY_PROFILE)); 1550 if (format.containsKey(MediaFormat.KEY_AAC_PROFILE)) { 1551 int profile = format.getInteger(MediaFormat.KEY_AAC_PROFILE, -1); 1552 assertEquals("mismatched KEY_AAC_PROFILE in file " + file, 1553 mProfile, profile); 1554 } 1555 if (format.containsKey(MediaFormat.KEY_PROFILE)) { 1556 int profile = format.getInteger(MediaFormat.KEY_PROFILE, -1); 1557 assertEquals("mismatched KEY_PROFILE in file " + file, mProfile, profile); 1558 } 1559 } else if (mMediaType.equals(MediaFormat.MIMETYPE_VIDEO_VP9)) { 1560 int profile = format.getInteger(MediaFormat.KEY_PROFILE, -1); 1561 assertEquals("mismatched KEY_PROFILE in file " + file, mProfile, profile); 1562 int level = format.getInteger(MediaFormat.KEY_LEVEL, -1); 1563 // The level information cannot be parsed from uncompressed frame header, so 1564 // verify the level information only if it is present. 1565 if (level != -1) { 1566 assertEquals("mismatched KEY_LEVEL in file " + file, mLevel, level); 1567 } 1568 } else { 1569 int profile = format.getInteger(MediaFormat.KEY_PROFILE, -1); 1570 assertEquals("mismatched KEY_PROFILE in file " + file, mProfile, profile); 1571 int level = format.getInteger(MediaFormat.KEY_LEVEL, -1); 1572 assertEquals("mismatched KEY_LEVEL in file " + file, mLevel, level); 1573 } 1574 if (mMediaType.startsWith("audio/")) { 1575 int sample_rate = format.getInteger(MediaFormat.KEY_SAMPLE_RATE, -1); 1576 assertEquals("mismatched KEY_SAMPLE_RATE in file " + file, 1577 mWR, sample_rate); 1578 int channel_count = format.getInteger(MediaFormat.KEY_CHANNEL_COUNT, -1); 1579 assertEquals("mismatched KEY_CHANNEL_COUNT in file " + file, 1580 mHCh, channel_count); 1581 } else if (mMediaType.startsWith("video/")) { 1582 int width = format.getInteger(MediaFormat.KEY_WIDTH, -1); 1583 assertEquals("mismatched KEY_WIDTH in file " + file, mWR, width); 1584 int height = format.getInteger(MediaFormat.KEY_HEIGHT, -1); 1585 assertEquals("mismatched KEY_HEIGHT in file " + file, mHCh, height); 1586 } 1587 } 1588 } 1589 } 1590 1591 /** 1592 * Makes sure if PTS(order) of a file matches the expected values in the corresponding text 1593 * file with just PTS values. 1594 */ 1595 @RunWith(Parameterized.class) 1596 public static class ExtractorTimeStampTest { 1597 private final String mRefFile; 1598 private final String mPTSListFile; 1599 private int mTrackIndex; 1600 // Allowing tolerance of +1/-1 for rounding error. 1601 private static final int PTS_TOLERANCE = 1; 1602 1603 public ExtractorTimeStampTest(String refFile, String textFile, int trackIndex) { 1604 mRefFile = refFile; 1605 mPTSListFile = textFile; 1606 mTrackIndex = trackIndex; 1607 } 1608 1609 @Parameterized.Parameters 1610 public static Collection<Object[]> input() { 1611 final List<Object[]> exhaustiveArgsList = Arrays.asList(new Object[][]{ 1612 {"bbb_384x216_768kbps_30fps_avc_2b.mp4", 1613 "pts_bbb_384x216_768kbps_30fps_avc_2b.txt", 0}, 1614 {"bbb_384x216_768kbps_25fps_avc_7b.mp4", 1615 "pts_bbb_384x216_768kbps_25fps_avc_7b.txt", 0}, 1616 {"bbb_384x216_768kbps_24fps_avc_5b.mkv", 1617 "pts_bbb_384x216_768kbps_24fps_avc_5b.txt", 0}, 1618 {"bbb_384x216_768kbps_30fps_avc_badapt.mkv", 1619 "pts_bbb_384x216_768kbps_30fps_avc_badapt.txt", 0}, 1620 {"bbb_384x216_768kbps_30fps_avc_2b.3gp", 1621 "pts_bbb_384x216_768kbps_30fps_avc_2b.txt", 0}, 1622 {"bbb_384x216_768kbps_25fps_avc_7b.3gp", 1623 "pts_bbb_384x216_768kbps_25fps_avc_7b.txt", 0}, 1624 {"bbb_384x216_768kbps_30fps_avc_badapt_bbb_480x360_768kbps_24fps_avc_5b.mkv", 1625 "pts_bbb_384x216_768kbps_30fps_avc_badapt.txt", 0}, 1626 {"bbb_384x216_768kbps_30fps_avc_badapt_bbb_480x360_768kbps_24fps_avc_5b.mkv", 1627 "pts_bbb_480x360_768kbps_24fps_avc_5b.txt", 1}, 1628 {"bbb_384x216_768kbps_30fps_avc_2b_bbb_cif_768kbps_25fps_avc_7b.mp4", 1629 "pts_bbb_384x216_768kbps_30fps_avc_2b.txt", 0}, 1630 {"bbb_384x216_768kbps_30fps_avc_2b_bbb_cif_768kbps_25fps_avc_7b.mp4", 1631 "pts_bbb_cif_768kbps_25fps_avc_7b.txt", 1}, 1632 {"bbb_384x216_768kbps_30fps_hevc_2b.mp4", 1633 "pts_bbb_384x216_768kbps_30fps_hevc_2b.txt", 0}, 1634 {"bbb_384x216_768kbps_25fps_hevc_7b.mp4", 1635 "pts_bbb_384x216_768kbps_25fps_hevc_7b.txt", 0}, 1636 {"bbb_384x216_768kbps_24fps_hevc_5b.mkv", 1637 "pts_bbb_384x216_768kbps_24fps_hevc_5b.txt", 0}, 1638 {"bbb_384x216_768kbps_30fps_hevc_badapt.mkv", 1639 "pts_bbb_384x216_768kbps_30fps_hevc_badapt.txt", 0}, 1640 {"bbb_384x216_768kbps_30fps_hevc_badapt_bbb_480x360_768kbps_24fps_hevc_5b.mkv", 1641 "pts_bbb_384x216_768kbps_30fps_hevc_badapt.txt", 0}, 1642 {"bbb_384x216_768kbps_30fps_hevc_badapt_bbb_480x360_768kbps_24fps_hevc_5b.mkv", 1643 "pts_bbb_480x360_768kbps_24fps_hevc_5b.txt", 1}, 1644 {"bbb_384x216_768kbps_30fps_hevc_2b_bbb_cif_768kbps_25fps_hevc_7b.mp4", 1645 "pts_bbb_384x216_768kbps_30fps_hevc_2b.txt", 0}, 1646 {"bbb_384x216_768kbps_30fps_hevc_2b_bbb_cif_768kbps_25fps_hevc_7b.mp4", 1647 "pts_bbb_cif_768kbps_25fps_hevc_7b.txt", 1}, 1648 {"bbb_384x216_768kbps_30fps_mpeg2_2b.mp4", 1649 "pts_bbb_384x216_768kbps_30fps_mpeg2_2b.txt", 0}, 1650 {"bbb_384x216_768kbps_25fps_mpeg2_5b.mp4", 1651 "pts_bbb_384x216_768kbps_25fps_mpeg2_5b.txt", 0}, 1652 {"bbb_384x216_768kbps_24fps_mpeg2_5b.mkv", 1653 "pts_bbb_384x216_768kbps_24fps_mpeg2_5b.txt", 0}, 1654 {"bbb_384x216_768kbps_30fps_mpeg2_2b.ts", 1655 "pts_bbb_384x216_768kbps_30fps_mpeg2_2b.txt", 0}, 1656 {"bbb_384x216_768kbps_25fps_mpeg2_7b.ts", 1657 "pts_bbb_384x216_768kbps_25fps_mpeg2_7b.txt", 0}, 1658 {"bbb_cif_768kbps_30fps_vp8_stereo_48kHz_192kbps_vorbis.webm", 1659 "pts_bbb_cif_768kbps_30fps_vp8.txt", 0}, 1660 {"bbb_cif_768kbps_30fps_vp8_stereo_48kHz_192kbps_vorbis.mkv", 1661 "pts_bbb_cif_768kbps_30fps_vp8.txt", 0}, 1662 {"bbb_cif_768kbps_30fps_vp8_stereo_48kHz_192kbps_vorbis.webm", 1663 "pts_stereo_48kHz_192kbps_vorbis.txt", 1}, 1664 {"bbb_cif_768kbps_30fps_vp8_stereo_48kHz_192kbps_vorbis.mkv", 1665 "pts_stereo_48kHz_192kbps_vorbis.txt", 1}, 1666 {"bbb_340x280_768kbps_30fps_split_non_display_frame_vp9.webm", 1667 "pts_bbb_340x280_768kbps_30fps_split_non_display_frame_vp9.txt", 0}, 1668 {"bbb_cif_768kbps_30fps_vp9_stereo_48kHz_192kbps_opus.webm", 1669 "pts_bbb_cif_768kbps_30fps_vp9.txt", 0}, 1670 {"bbb_cif_768kbps_30fps_vp9_stereo_48kHz_192kbps_opus.mkv", 1671 "pts_bbb_cif_768kbps_30fps_vp9.txt", 0}, 1672 {"bbb_cif_768kbps_30fps_av1.mp4", 1673 "pts_bbb_cif_768kbps_30fps_av1.txt", 0}, 1674 {"bbb_cif_768kbps_30fps_av1.mkv", 1675 "pts_bbb_cif_768kbps_30fps_av1.txt", 0}, 1676 {"bbb_cif_768kbps_30fps_av1.webm", 1677 "pts_bbb_cif_768kbps_30fps_av1.txt", 0}, 1678 {"binary_counter_320x240_30fps_600frames.mp4", 1679 "pts_binary_counter_320x240_30fps_600frames.txt", 0}, 1680 }); 1681 return exhaustiveArgsList; 1682 } 1683 1684 @ApiTest(apis = {"android.media.MediaExtractor#getSampleTime"}) 1685 @LargeTest 1686 @Test 1687 public void testPresentationTimeStampsMatch() throws IOException { 1688 try (FileInputStream file = new FileInputStream(MEDIA_DIR + mPTSListFile); 1689 InputStreamReader input = new InputStreamReader(file); 1690 Reader txtRdr = new BufferedReader(input)) { 1691 StreamTokenizer strTok = new StreamTokenizer(txtRdr); 1692 strTok.parseNumbers(); 1693 1694 MediaExtractor extractor = new MediaExtractor(); 1695 Preconditions.assertTestFileExists(MEDIA_DIR + mRefFile); 1696 extractor.setDataSource(MEDIA_DIR + mRefFile); 1697 assertTrue(mTrackIndex < extractor.getTrackCount()); 1698 extractor.selectTrack(mTrackIndex); 1699 while (true) { 1700 if (strTok.nextToken() == StreamTokenizer.TT_EOF) break; 1701 assertTrue("PTS mismatch exp/got: " + (long) strTok.nval + "/" + 1702 extractor.getSampleTime(), 1703 Math.abs(extractor.getSampleTime() - (long) strTok.nval) <= 1704 PTS_TOLERANCE); 1705 if (!extractor.advance()) break; 1706 } 1707 assertEquals(StreamTokenizer.TT_EOF, strTok.nextToken()); 1708 assertTrue(!extractor.advance()); 1709 extractor.release(); 1710 } 1711 } 1712 } 1713 } 1714