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