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