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