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