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