1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.mediav2.cts;
18 
19 import android.content.Context;
20 import android.content.res.AssetFileDescriptor;
21 import android.media.MediaCodec;
22 import android.media.MediaDataSource;
23 import android.media.MediaExtractor;
24 import android.media.MediaFormat;
25 import android.net.Uri;
26 import android.os.ParcelFileDescriptor;
27 import android.os.PersistableBundle;
28 import android.util.Log;
29 
30 import androidx.test.filters.LargeTest;
31 import androidx.test.filters.SmallTest;
32 import androidx.test.platform.app.InstrumentationRegistry;
33 
34 import org.junit.After;
35 import org.junit.Before;
36 import org.junit.Ignore;
37 import org.junit.Rule;
38 import org.junit.Test;
39 import org.junit.experimental.runners.Enclosed;
40 import org.junit.rules.TestName;
41 import org.junit.runner.RunWith;
42 import org.junit.runners.Parameterized;
43 
44 import java.io.File;
45 import java.io.FileInputStream;
46 import java.io.FileOutputStream;
47 import java.io.IOException;
48 import java.nio.ByteBuffer;
49 import java.util.ArrayList;
50 import java.util.Arrays;
51 import java.util.Collection;
52 import java.util.Collections;
53 import java.util.List;
54 import java.util.Random;
55 
56 import static org.junit.Assert.assertTrue;
57 import static org.junit.Assert.fail;
58 import static org.junit.Assume.assumeTrue;
59 
60 class TestMediaDataSource extends MediaDataSource {
61     private static final String LOG_TAG = TestMediaDataSource.class.getSimpleName();
62     private static final boolean ENABLE_LOGS = false;
63     private byte[] mData;
64     private boolean mFatalGetSize;
65     private boolean mFatalReadAt;
66     private boolean mIsClosed = false;
67 
fromString(String inpPath, boolean failSize, boolean failRead)68     static TestMediaDataSource fromString(String inpPath, boolean failSize, boolean failRead)
69             throws IOException {
70         try (FileInputStream fInp = new FileInputStream(inpPath)) {
71             int size = (int) new File(inpPath).length();
72             byte[] data = new byte[size];
73             fInp.read(data, 0, size);
74             return new TestMediaDataSource(data, failSize, failRead);
75         }
76     }
77 
TestMediaDataSource(byte[] data, boolean fatalGetSize, boolean fatalReadAt)78     private TestMediaDataSource(byte[] data, boolean fatalGetSize, boolean fatalReadAt) {
79         mData = data;
80         mFatalGetSize = fatalGetSize;
81         mFatalReadAt = fatalReadAt;
82     }
83 
84     @Override
readAt(long srcOffset, byte[] buffer, int dstOffset, int size)85     public synchronized int readAt(long srcOffset, byte[] buffer, int dstOffset, int size)
86             throws IOException {
87         if (mFatalReadAt) {
88             throw new IOException("malformed media data source");
89         }
90         if (srcOffset >= mData.length) {
91             return -1;
92         }
93         if (srcOffset + size > mData.length) {
94             size = mData.length - (int) srcOffset;
95         }
96         System.arraycopy(mData, (int) srcOffset, buffer, dstOffset, size);
97         return size;
98     }
99 
100     @Override
getSize()101     public synchronized long getSize() throws IOException {
102         if (mFatalGetSize) {
103             throw new IOException("malformed media data source");
104         }
105         if (ENABLE_LOGS) {
106             Log.v(LOG_TAG, "getSize: " + mData.length);
107         }
108         return mData.length;
109     }
110 
111     @Override
close()112     public synchronized void close() {
113         mIsClosed = true;
114     }
115 
isClosed()116     public boolean isClosed() {
117         return mIsClosed;
118     }
119 }
120 
121 @RunWith(Enclosed.class)
122 public class ExtractorTest {
123     private static final String LOG_TAG = ExtractorTest.class.getSimpleName();
124     private static final boolean ENABLE_LOGS = false;
125     private static final int MAX_SAMPLE_SIZE = 4 * 1024 * 1024;
126     private static final String EXT_SEL_KEY = "ext-sel";
127     static private final List<String> codecListforTypeMp4 =
128             Arrays.asList(MediaFormat.MIMETYPE_AUDIO_MPEG, MediaFormat.MIMETYPE_AUDIO_AAC,
129                     MediaFormat.MIMETYPE_AUDIO_FLAC, MediaFormat.MIMETYPE_AUDIO_VORBIS,
130                     MediaFormat.MIMETYPE_AUDIO_OPUS, MediaFormat.MIMETYPE_VIDEO_MPEG2,
131                     MediaFormat.MIMETYPE_VIDEO_MPEG4, MediaFormat.MIMETYPE_VIDEO_H263,
132                     MediaFormat.MIMETYPE_VIDEO_AVC, MediaFormat.MIMETYPE_VIDEO_HEVC);
133     static private final List<String> codecListforTypeWebm =
134             Arrays.asList(MediaFormat.MIMETYPE_AUDIO_VORBIS, MediaFormat.MIMETYPE_AUDIO_OPUS,
135                     MediaFormat.MIMETYPE_VIDEO_VP8, MediaFormat.MIMETYPE_VIDEO_VP9);
136     static private final List<String> codecListforType3gp =
137             Arrays.asList(MediaFormat.MIMETYPE_AUDIO_AAC, MediaFormat.MIMETYPE_AUDIO_AMR_NB,
138                     MediaFormat.MIMETYPE_AUDIO_AMR_WB, MediaFormat.MIMETYPE_VIDEO_MPEG4,
139                     MediaFormat.MIMETYPE_VIDEO_H263, MediaFormat.MIMETYPE_VIDEO_AVC);
140     static private final List<String> codecListforTypeMkv =
141             Arrays.asList(MediaFormat.MIMETYPE_AUDIO_MPEG, MediaFormat.MIMETYPE_AUDIO_AAC,
142                     MediaFormat.MIMETYPE_AUDIO_FLAC, MediaFormat.MIMETYPE_AUDIO_VORBIS,
143                     MediaFormat.MIMETYPE_AUDIO_OPUS, MediaFormat.MIMETYPE_VIDEO_MPEG2,
144                     MediaFormat.MIMETYPE_VIDEO_MPEG4, MediaFormat.MIMETYPE_VIDEO_H263,
145                     MediaFormat.MIMETYPE_VIDEO_AVC, MediaFormat.MIMETYPE_VIDEO_HEVC,
146                     MediaFormat.MIMETYPE_VIDEO_VP8, MediaFormat.MIMETYPE_VIDEO_VP9);
147     static private final List<String> codecListforTypeOgg =
148             Arrays.asList(MediaFormat.MIMETYPE_AUDIO_VORBIS, MediaFormat.MIMETYPE_AUDIO_OPUS);
149     static private final List<String> codecListforTypeTs =
150             Arrays.asList(MediaFormat.MIMETYPE_AUDIO_AAC, MediaFormat.MIMETYPE_VIDEO_MPEG2,
151                     MediaFormat.MIMETYPE_VIDEO_AVC);
152     static private final List<String> codecListforTypeRaw =
153             Arrays.asList(MediaFormat.MIMETYPE_AUDIO_AAC, MediaFormat.MIMETYPE_AUDIO_FLAC,
154                     MediaFormat.MIMETYPE_AUDIO_MPEG, MediaFormat.MIMETYPE_AUDIO_AMR_NB,
155                     MediaFormat.MIMETYPE_AUDIO_AMR_WB, MediaFormat.MIMETYPE_AUDIO_RAW);
156     // List of codecs that are not required to be supported as per CDD but are tested
157     static private final List<String> codecListSupp =
158             Arrays.asList(MediaFormat.MIMETYPE_VIDEO_AV1);
159     private static String mInpPrefix = WorkDir.getMediaDirString();
160     private static String extSel;
161 
162     static {
163         android.os.Bundle args = InstrumentationRegistry.getArguments();
164         final String defSel = "mp4;webm;3gp;mkv;ogg;supp";
165         extSel = (null == args.getString(EXT_SEL_KEY)) ? defSel : args.getString(EXT_SEL_KEY);
166     }
167 
shouldRunTest(String mime)168     static private boolean shouldRunTest(String mime) {
169         boolean result = false;
170         if ((extSel.contains("mp4") && codecListforTypeMp4.contains(mime)) ||
171                 (extSel.contains("webm") && codecListforTypeWebm.contains(mime)) ||
172                 (extSel.contains("3gp") && codecListforType3gp.contains(mime)) ||
173                 (extSel.contains("mkv") && codecListforTypeMkv.contains(mime)) ||
174                 (extSel.contains("ogg") && codecListforTypeOgg.contains(mime)) ||
175                 (extSel.contains("supp") && codecListSupp.contains(mime)))
176             result = true;
177         return result;
178     }
179 
isExtractorOKonEOS(MediaExtractor extractor)180     private static boolean isExtractorOKonEOS(MediaExtractor extractor) {
181         return extractor.getSampleTrackIndex() < 0 && extractor.getSampleSize() < 0 &&
182                 extractor.getSampleFlags() < 0 && extractor.getSampleTime() < 0;
183     }
184 
isSampleInfoIdentical(MediaCodec.BufferInfo refSample, MediaCodec.BufferInfo testSample)185     private static boolean isSampleInfoIdentical(MediaCodec.BufferInfo refSample,
186             MediaCodec.BufferInfo testSample) {
187         return refSample.flags == testSample.flags && refSample.size == testSample.size &&
188                 refSample.presentationTimeUs == testSample.presentationTimeUs;
189     }
190 
isSampleInfoValidAndIdentical(MediaCodec.BufferInfo refSample, MediaCodec.BufferInfo testSample)191     private static boolean isSampleInfoValidAndIdentical(MediaCodec.BufferInfo refSample,
192             MediaCodec.BufferInfo testSample) {
193         return refSample.flags == testSample.flags && refSample.size == testSample.size &&
194                 Math.abs(refSample.presentationTimeUs - testSample.presentationTimeUs) <= 1 &&
195                 refSample.flags >= 0 && refSample.size >= 0 && refSample.presentationTimeUs >= 0;
196     }
197 
isCSDIdentical(MediaFormat refFormat, MediaFormat testFormat)198     static boolean isCSDIdentical(MediaFormat refFormat, MediaFormat testFormat) {
199         String mime = refFormat.getString(MediaFormat.KEY_MIME);
200         for (int i = 0; ; i++) {
201             String csdKey = "csd-" + i;
202             boolean refHasCSD = refFormat.containsKey(csdKey);
203             boolean testHasCSD = testFormat.containsKey(csdKey);
204             if (refHasCSD != testHasCSD) {
205                 if (ENABLE_LOGS) {
206                     Log.w(LOG_TAG, "error, ref fmt has CSD: " + refHasCSD + " test fmt has CSD: " +
207                             testHasCSD);
208                 }
209                 return false;
210             }
211             if (refHasCSD) {
212                 Log.v(LOG_TAG, mime + " has " + csdKey);
213                 ByteBuffer r = refFormat.getByteBuffer(csdKey);
214                 ByteBuffer t = testFormat.getByteBuffer(csdKey);
215                 if (!r.equals(t)) {
216                     if (ENABLE_LOGS) {
217                         Log.w(LOG_TAG, "ref CSD and test CSD buffers are not identical");
218                     }
219                     return false;
220                 }
221             } else break;
222         }
223         return true;
224     }
225 
isFormatSimilar(MediaFormat refFormat, MediaFormat testFormat)226     private static boolean isFormatSimilar(MediaFormat refFormat, MediaFormat testFormat) {
227         String refMime = refFormat.getString(MediaFormat.KEY_MIME);
228         String testMime = testFormat.getString(MediaFormat.KEY_MIME);
229 
230         if (!refMime.equals(testMime)) return false;
231         if (!isCSDIdentical(refFormat, testFormat)) return false;
232         if (refMime.startsWith("audio/")) {
233             return refFormat.getInteger(MediaFormat.KEY_CHANNEL_COUNT) ==
234                     testFormat.getInteger(MediaFormat.KEY_CHANNEL_COUNT) &&
235                     refFormat.getInteger(MediaFormat.KEY_SAMPLE_RATE) ==
236                             testFormat.getInteger(MediaFormat.KEY_SAMPLE_RATE);
237         } else if (refMime.startsWith("video/")) {
238             return refFormat.getInteger(MediaFormat.KEY_WIDTH) ==
239                     testFormat.getInteger(MediaFormat.KEY_WIDTH) &&
240                     refFormat.getInteger(MediaFormat.KEY_HEIGHT) ==
241                             testFormat.getInteger(MediaFormat.KEY_HEIGHT);
242         }
243         return true;
244     }
245 
isMediaSimilar(MediaExtractor refExtractor, MediaExtractor testExtractor, String mime, int sampleLimit)246     private static boolean isMediaSimilar(MediaExtractor refExtractor, MediaExtractor testExtractor,
247             String mime, int sampleLimit) {
248         ByteBuffer refBuffer = ByteBuffer.allocate(MAX_SAMPLE_SIZE);
249         ByteBuffer testBuffer = ByteBuffer.allocate(MAX_SAMPLE_SIZE);
250 
251         int noOfTracksMatched = 0;
252         for (int refTrackID = 0; refTrackID < refExtractor.getTrackCount(); refTrackID++) {
253             MediaFormat refFormat = refExtractor.getTrackFormat(refTrackID);
254             String refMime = refFormat.getString(MediaFormat.KEY_MIME);
255             if (mime != null && !refMime.equals(mime)) {
256                 continue;
257             }
258             for (int testTrackID = 0; testTrackID < testExtractor.getTrackCount(); testTrackID++) {
259                 MediaFormat testFormat = testExtractor.getTrackFormat(testTrackID);
260                 if (!isFormatSimilar(refFormat, testFormat)) {
261                     continue;
262                 }
263                 refExtractor.selectTrack(refTrackID);
264                 testExtractor.selectTrack(testTrackID);
265 
266                 MediaCodec.BufferInfo refSampleInfo = new MediaCodec.BufferInfo();
267                 MediaCodec.BufferInfo testSampleInfo = new MediaCodec.BufferInfo();
268                 boolean areTracksIdentical = true;
269                 for (int frameCount = 0; ; frameCount++) {
270                     refSampleInfo.set(0, (int) refExtractor.getSampleSize(),
271                             refExtractor.getSampleTime(), refExtractor.getSampleFlags());
272                     testSampleInfo.set(0, (int) testExtractor.getSampleSize(),
273                             testExtractor.getSampleTime(), testExtractor.getSampleFlags());
274                     if (!isSampleInfoValidAndIdentical(refSampleInfo, testSampleInfo)) {
275                         if (ENABLE_LOGS) {
276                             Log.d(LOG_TAG,
277                                     " Mime: " + refMime + " mismatch for sample: " + frameCount);
278                             Log.d(LOG_TAG, " flags exp/got: " +
279                                     refSampleInfo.flags + '/' + testSampleInfo.flags);
280                             Log.d(LOG_TAG, " size exp/got: " +
281                                     refSampleInfo.size + '/' + testSampleInfo.size);
282                             Log.d(LOG_TAG, " ts exp/got: " + refSampleInfo.presentationTimeUs +
283                                     '/' + testSampleInfo.presentationTimeUs);
284                         }
285                         areTracksIdentical = false;
286                         break;
287                     }
288                     int refSz = refExtractor.readSampleData(refBuffer, 0);
289                     if (refSz != refSampleInfo.size) {
290                         if (ENABLE_LOGS) {
291                             Log.d(LOG_TAG, "Mime: " + refMime + " Size exp/got: " +
292                                     refSampleInfo.size + '/' + refSz);
293                         }
294                         areTracksIdentical = false;
295                         break;
296                     }
297                     int testSz = testExtractor.readSampleData(testBuffer, 0);
298                     if (testSz != testSampleInfo.size) {
299                         if (ENABLE_LOGS) {
300                             Log.d(LOG_TAG, "Mime: " + refMime + " Size exp/got: " +
301                                     testSampleInfo.size + '/' + testSz);
302                         }
303                         areTracksIdentical = false;
304                         break;
305                     }
306                     int trackIndex = refExtractor.getSampleTrackIndex();
307                     if (trackIndex != refTrackID) {
308                         if (ENABLE_LOGS) {
309                             Log.d(LOG_TAG, "Mime: " + refMime +
310                                     " TrackID exp/got: " + refTrackID + '/' + trackIndex);
311                         }
312                         areTracksIdentical = false;
313                         break;
314                     }
315                     trackIndex = testExtractor.getSampleTrackIndex();
316                     if (trackIndex != testTrackID) {
317                         if (ENABLE_LOGS) {
318                             Log.d(LOG_TAG, "Mime: " + refMime +
319                                     " TrackID exp/got: " + testTrackID + '/' + trackIndex);
320                         }
321                         areTracksIdentical = false;
322                         break;
323                     }
324                     if (!testBuffer.equals(refBuffer)) {
325                         if (ENABLE_LOGS) {
326                             Log.d(LOG_TAG, "Mime: " + refMime + " sample data is not identical");
327                         }
328                         areTracksIdentical = false;
329                         break;
330                     }
331                     boolean haveRefSamples = refExtractor.advance();
332                     boolean haveTestSamples = testExtractor.advance();
333                     if (haveRefSamples != haveTestSamples) {
334                         if (ENABLE_LOGS) {
335                             Log.d(LOG_TAG, "Mime: " + refMime + " Mismatch in sampleCount");
336                         }
337                         areTracksIdentical = false;
338                         break;
339                     }
340 
341                     if (!haveRefSamples && !isExtractorOKonEOS(refExtractor)) {
342                         if (ENABLE_LOGS) {
343                             Log.d(LOG_TAG, "Mime: " + refMime + " calls post advance() are not OK");
344                         }
345                         areTracksIdentical = false;
346                         break;
347                     }
348                     if (!haveTestSamples && !isExtractorOKonEOS(testExtractor)) {
349                         if (ENABLE_LOGS) {
350                             Log.d(LOG_TAG, "Mime: " + refMime + " calls post advance() are not OK");
351                         }
352                         areTracksIdentical = false;
353                         break;
354                     }
355                     if (ENABLE_LOGS) {
356                         Log.v(LOG_TAG, "Mime: " + refMime + " Sample: " + frameCount +
357                                 " flags: " + refSampleInfo.flags +
358                                 " size: " + refSampleInfo.size +
359                                 " ts: " + refSampleInfo.presentationTimeUs);
360                     }
361                     if (!haveRefSamples || frameCount >= sampleLimit) {
362                         break;
363                     }
364                 }
365                 testExtractor.unselectTrack(testTrackID);
366                 refExtractor.unselectTrack(refTrackID);
367                 if (areTracksIdentical) {
368                     noOfTracksMatched++;
369                     break;
370                 }
371             }
372             if (mime != null && noOfTracksMatched > 0) break;
373         }
374         if (mime == null) {
375             return noOfTracksMatched == refExtractor.getTrackCount();
376         } else {
377             return noOfTracksMatched > 0;
378         }
379     }
380 
381     /**
382      * Tests setDataSource(...) Api by observing the extractor behavior after its successful
383      * instantiation using a media stream.
384      */
385     @SmallTest
386     public static class SetDataSourceTest {
387         @Rule
388         public TestName testName = new TestName();
389 
390         private static final String mInpMedia = "ForBiggerEscapes.mp4";
391         private static final String mInpMediaUrl =
392                 "http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerEscapes.mp4";
393 
394         private MediaExtractor mRefExtractor;
395 
396         static {
397             System.loadLibrary("ctsmediav2extractor_jni");
398         }
399 
400         @Before
setUp()401         public void setUp() throws IOException {
402             mRefExtractor = new MediaExtractor();
403             mRefExtractor.setDataSource(mInpPrefix + mInpMedia);
404         }
405 
406         @After
tearDown()407         public void tearDown() {
408             mRefExtractor.release();
409             mRefExtractor = null;
410         }
411 
areMetricsIdentical(MediaExtractor refExtractor, MediaExtractor testExtractor)412         private static boolean areMetricsIdentical(MediaExtractor refExtractor,
413                 MediaExtractor testExtractor) {
414             PersistableBundle bundle = refExtractor.getMetrics();
415             int refNumTracks = bundle.getInt(MediaExtractor.MetricsConstants.TRACKS);
416             String refFormat = bundle.getString(MediaExtractor.MetricsConstants.FORMAT);
417             String refMime = bundle.getString(MediaExtractor.MetricsConstants.MIME_TYPE);
418             bundle = testExtractor.getMetrics();
419             int testNumTracks = bundle.getInt(MediaExtractor.MetricsConstants.TRACKS);
420             String testFormat = bundle.getString(MediaExtractor.MetricsConstants.FORMAT);
421             String testMime = bundle.getString(MediaExtractor.MetricsConstants.MIME_TYPE);
422             boolean result = testNumTracks == refNumTracks && testFormat.equals(refFormat) &&
423                     testMime.equals(refMime);
424             if (ENABLE_LOGS) {
425                 Log.d(LOG_TAG, " NumTracks exp/got: " + refNumTracks + '/' + testNumTracks);
426                 Log.d(LOG_TAG, " Format exp/got: " + refFormat + '/' + testFormat);
427                 Log.d(LOG_TAG, " Mime exp/got: " + refMime + '/' + testMime);
428             }
429             return result;
430         }
431 
isSeekOk(MediaExtractor refExtractor, MediaExtractor testExtractor)432         private static boolean isSeekOk(MediaExtractor refExtractor, MediaExtractor testExtractor) {
433             final long maxEstDuration = 14000000;
434             final int MAX_SEEK_POINTS = 7;
435             final long mSeed = 0x12b9b0a1;
436             final Random randNum = new Random(mSeed);
437             MediaCodec.BufferInfo refSampleInfo = new MediaCodec.BufferInfo();
438             MediaCodec.BufferInfo testSampleInfo = new MediaCodec.BufferInfo();
439             boolean result = true;
440             for (int trackID = 0; trackID < refExtractor.getTrackCount() && result; trackID++) {
441                 refExtractor.selectTrack(trackID);
442                 testExtractor.selectTrack(trackID);
443                 for (int i = 0; i < MAX_SEEK_POINTS && result; i++) {
444                     long pts = (long) (randNum.nextDouble() * maxEstDuration);
445                     for (int mode = MediaExtractor.SEEK_TO_PREVIOUS_SYNC;
446                          mode <= MediaExtractor.SEEK_TO_CLOSEST_SYNC; mode++) {
447                         refExtractor.seekTo(pts, mode);
448                         testExtractor.seekTo(pts, mode);
449                         refSampleInfo.set(0, (int) refExtractor.getSampleSize(),
450                                 refExtractor.getSampleTime(), refExtractor.getSampleFlags());
451                         testSampleInfo.set(0, (int) testExtractor.getSampleSize(),
452                                 testExtractor.getSampleTime(), testExtractor.getSampleFlags());
453                         result = isSampleInfoIdentical(refSampleInfo, testSampleInfo);
454                         int refTrackIdx = refExtractor.getSampleTrackIndex();
455                         int testTrackIdx = testExtractor.getSampleTrackIndex();
456                         result &= (refTrackIdx == testTrackIdx);
457                         if (ENABLE_LOGS) {
458                             Log.d(LOG_TAG, " mode/pts/trackId:" + mode + "/" + pts + "/" + trackID);
459                             Log.d(LOG_TAG, " trackId exp/got: " + refTrackIdx + '/' + testTrackIdx);
460                             Log.d(LOG_TAG, " flags exp/got: " +
461                                     refSampleInfo.flags + '/' + testSampleInfo.flags);
462                             Log.d(LOG_TAG, " size exp/got: " +
463                                     refSampleInfo.size + '/' + testSampleInfo.size);
464                             Log.d(LOG_TAG, " ts exp/got: " + refSampleInfo.presentationTimeUs +
465                                     '/' + testSampleInfo.presentationTimeUs);
466                         }
467                     }
468                 }
469                 refExtractor.unselectTrack(trackID);
470                 testExtractor.unselectTrack(trackID);
471             }
472             return result;
473         }
474 
475         @Test
testAssetFD()476         public void testAssetFD() throws IOException {
477             File inpFile = new File(mInpPrefix + mInpMedia);
478             MediaExtractor testExtractor = new MediaExtractor();
479             try (ParcelFileDescriptor parcelFD = ParcelFileDescriptor
480                     .open(inpFile, ParcelFileDescriptor.MODE_READ_ONLY);
481                  AssetFileDescriptor afd = new AssetFileDescriptor(parcelFD, 0,
482                          AssetFileDescriptor.UNKNOWN_LENGTH)) {
483                 testExtractor.setDataSource(afd);
484             }
485             assertTrue(testExtractor.getCachedDuration() < 0);
486             if (!isMediaSimilar(mRefExtractor, testExtractor, null, Integer.MAX_VALUE) ||
487                     !areMetricsIdentical(mRefExtractor, testExtractor) ||
488                     !isSeekOk(mRefExtractor, testExtractor)) {
489                 fail("setDataSource failed: " + testName.getMethodName());
490             }
491             testExtractor.release();
492         }
493 
494         @Test
495         public void testFileDescriptor() throws IOException {
496             File inpFile = new File(mInpPrefix + mInpMedia);
497             MediaExtractor testExtractor = new MediaExtractor();
498             try (FileInputStream fInp = new FileInputStream(inpFile)) {
499                 testExtractor.setDataSource(fInp.getFD());
500             }
501             assertTrue(testExtractor.getCachedDuration() < 0);
502             if (!isMediaSimilar(mRefExtractor, testExtractor, null, Integer.MAX_VALUE) ||
503                     !areMetricsIdentical(mRefExtractor, testExtractor) ||
504                     !isSeekOk(mRefExtractor, testExtractor)) {
505                 fail("setDataSource failed: " + testName.getMethodName());
506             }
507             testExtractor.release();
508         }
509 
510         @Test
511         public void testFileDescriptorLenOffset() throws IOException {
512             File inpFile = new File(mInpPrefix + mInpMedia);
513             File outFile = File.createTempFile("temp", ".out");
514             byte[] garbageAppend = "PrefixGarbage".getBytes();
515             try (FileInputStream fInp = new FileInputStream(inpFile);
516                  FileOutputStream fOut = new FileOutputStream(outFile)) {
517                 fOut.write(garbageAppend);
518                 byte[] data = new byte[(int) new File(inpFile.toString()).length()];
519                 if (fInp.read(data) == -1) {
520                     fail("Failed to read input file");
521                 }
522                 fOut.write(data);
523                 fOut.write(garbageAppend);
524             }
525             MediaExtractor testExtractor = new MediaExtractor();
526             try (FileInputStream fInp = new FileInputStream(outFile)) {
527                 testExtractor.setDataSource(fInp.getFD(), garbageAppend.length,
528                         inpFile.length());
529             }
530             assertTrue(testExtractor.getCachedDuration() < 0);
531             if (!isMediaSimilar(mRefExtractor, testExtractor, null, Integer.MAX_VALUE) ||
532                     !areMetricsIdentical(mRefExtractor, testExtractor) ||
533                     !isSeekOk(mRefExtractor, testExtractor)) {
534                 fail("setDataSource failed: " + testName.getMethodName());
535             }
536             testExtractor.release();
537             outFile.delete();
538         }
539 
540         @Test
541         public void testMediaDataSource() throws Exception {
542             TestMediaDataSource dataSource =
543                     TestMediaDataSource.fromString(mInpPrefix + mInpMedia, false, false);
544             MediaExtractor testExtractor = new MediaExtractor();
545             testExtractor.setDataSource(dataSource);
546             assertTrue(testExtractor.getCachedDuration() < 0);
547             if (!isMediaSimilar(mRefExtractor, testExtractor, null, Integer.MAX_VALUE) ||
548                     !areMetricsIdentical(mRefExtractor, testExtractor) ||
549                     !isSeekOk(mRefExtractor, testExtractor)) {
550                 fail("setDataSource failed: " + testName.getMethodName());
551             }
552             testExtractor.release();
553             assertTrue(dataSource.isClosed());
554         }
555 
556         @Test
557         public void testContextUri() throws IOException {
558             Context context = InstrumentationRegistry.getInstrumentation().getContext();
559             String path = "android.resource://android.mediav2.cts/" + R.raw.forbiggerescapes;
560             MediaExtractor testExtractor = new MediaExtractor();
561             testExtractor.setDataSource(context, Uri.parse(path), null);
562             assertTrue(testExtractor.getCachedDuration() < 0);
563             if (!isMediaSimilar(mRefExtractor, testExtractor, null, Integer.MAX_VALUE) ||
564                     !areMetricsIdentical(mRefExtractor, testExtractor) ||
565                     !isSeekOk(mRefExtractor, testExtractor)) {
566                 fail("setDataSource failed: " + testName.getMethodName());
567             }
568             testExtractor.release();
569         }
570 
571         @Test
572         public void testUrlDataSource() throws Exception {
573             MediaExtractor testExtractor = new MediaExtractor();
574             testExtractor.setDataSource(mInpMediaUrl, null);
575             if (!isMediaSimilar(mRefExtractor, testExtractor, null, Integer.MAX_VALUE) ||
576                     !areMetricsIdentical(mRefExtractor, testExtractor) ||
577                     !isSeekOk(mRefExtractor, testExtractor)) {
578                 fail("setDataSource failed: " + testName.getMethodName());
579             }
580             testExtractor.selectTrack(0);
581             for (int idx = 0; ; idx++) {
582                 if ((idx & (idx - 1)) == 0) {
583                     long cachedDuration = testExtractor.getCachedDuration();
584                     if (ENABLE_LOGS) {
585                         Log.v(LOG_TAG, "cachedDuration at frame: " + idx + " is:" + cachedDuration);
586                     }
587                     assertTrue("cached duration should be non-negative", cachedDuration >= 0);
588                 }
589                 if (!testExtractor.advance()) break;
590             }
591             assertTrue(testExtractor.hasCacheReachedEndOfStream());
592             testExtractor.unselectTrack(0);
593             testExtractor.release();
594         }
595 
596         private native boolean nativeTestDataSource(String srcPath, String srcUrl);
597 
598         @Test
599         public void testDataSourceNative() {
600             assertTrue(testName.getMethodName() + " failed ",
601                     nativeTestDataSource(mInpPrefix + mInpMedia, mInpMediaUrl));
602         }
603     }
604 
605     /**
606      * Encloses extractor functionality tests
607      */
608     @RunWith(Parameterized.class)
609     public static class FunctionalityTest {
610         private static final int MAX_SEEK_POINTS = 7;
611         private static final long mSeed = 0x12b9b0a1;
612         private final Random mRandNum = new Random(mSeed);
613         private String[] mSrcFiles;
614         private String mMime;
615 
616         static {
617             System.loadLibrary("ctsmediav2extractor_jni");
618         }
619 
620         @Rule
621         public TestName testName = new TestName();
622 
623         @Parameterized.Parameters(name = "{index}({0})")
624         public static Collection<Object[]> input() {
625             /* TODO(b/157108639) - add missing test files */
626             return Arrays.asList(new Object[][]{
627                     {MediaFormat.MIMETYPE_VIDEO_MPEG2, new String[]{
628                             "bbb_cif_768kbps_30fps_mpeg2_stereo_48kHz_192kbps_mp3.mp4",
629                             "bbb_cif_768kbps_30fps_mpeg2.mkv",}},
630                     {MediaFormat.MIMETYPE_VIDEO_H263, new String[]{
631                             "bbb_cif_768kbps_30fps_h263.mp4",
632                             "bbb_cif_768kbps_30fps_h263_mono_8kHz_12kbps_amrnb.3gp",}},
633                     {MediaFormat.MIMETYPE_VIDEO_MPEG4, new String[]{
634                             "bbb_cif_768kbps_30fps_mpeg4_stereo_48kHz_192kbps_flac.mp4",
635                             "bbb_cif_768kbps_30fps_mpeg4_mono_16kHz_20kbps_amrwb.3gp",}},
636                     {MediaFormat.MIMETYPE_VIDEO_AVC, new String[]{
637                             "bbb_cif_768kbps_30fps_avc_stereo_48kHz_192kbps_vorbis.mp4",
638                             "bbb_cif_768kbps_30fps_avc_stereo_48kHz_192kbps_aac.mkv",
639                             "bbb_cif_768kbps_30fps_avc_stereo_48kHz_192kbps_aac.3gp",}},
640                     {MediaFormat.MIMETYPE_VIDEO_HEVC, new String[]{
641                             "bbb_cif_768kbps_30fps_hevc_stereo_48kHz_192kbps_opus.mp4",
642                             "bbb_cif_768kbps_30fps_hevc_stereo_48kHz_192kbps_mp3.mkv",}},
643                     {MediaFormat.MIMETYPE_VIDEO_VP8, new String[]{
644                             "bbb_cif_768kbps_30fps_vp8_stereo_48kHz_192kbps_vorbis.webm",
645                             "bbb_cif_768kbps_30fps_vp8_stereo_48kHz_192kbps_vorbis.mkv"}},
646                     {MediaFormat.MIMETYPE_VIDEO_VP9, new String[]{
647                             "bbb_cif_768kbps_30fps_vp9_stereo_48kHz_192kbps_opus.webm",
648                             "bbb_cif_768kbps_30fps_vp9_stereo_48kHz_192kbps_opus.mkv",}},
649                     {MediaFormat.MIMETYPE_VIDEO_AV1, new String[]{
650                             "bbb_cif_768kbps_30fps_av1.mp4",
651                             "bbb_cif_768kbps_30fps_av1.webm",
652                             "bbb_cif_768kbps_30fps_av1.mkv",}},
653                     {MediaFormat.MIMETYPE_AUDIO_VORBIS, new String[]{
654                             "bbb_cif_768kbps_30fps_avc_stereo_48kHz_192kbps_vorbis.mp4",
655                             "bbb_cif_768kbps_30fps_vp8_stereo_48kHz_192kbps_vorbis.mkv",
656                             "bbb_cif_768kbps_30fps_vp8_stereo_48kHz_192kbps_vorbis.webm",
657                             "bbb_stereo_48kHz_192kbps_vorbis.ogg",}},
658                     {MediaFormat.MIMETYPE_AUDIO_OPUS, new String[]{
659                             "bbb_cif_768kbps_30fps_vp9_stereo_48kHz_192kbps_opus.webm",
660                             "bbb_cif_768kbps_30fps_vp9_stereo_48kHz_192kbps_opus.mkv",
661                             "bbb_cif_768kbps_30fps_hevc_stereo_48kHz_192kbps_opus.mp4",
662                             "bbb_stereo_48kHz_192kbps_opus.ogg",}},
663                     {MediaFormat.MIMETYPE_AUDIO_MPEG, new String[]{
664                             "bbb_stereo_48kHz_192kbps_mp3.mp3",
665                             "bbb_cif_768kbps_30fps_mpeg2_stereo_48kHz_192kbps_mp3.mp4",
666                             "bbb_cif_768kbps_30fps_hevc_stereo_48kHz_192kbps_mp3.mkv",}},
667                     {MediaFormat.MIMETYPE_AUDIO_AAC, new String[]{
668                             "bbb_stereo_48kHz_192kbps_aac.mp4",
669                             "bbb_cif_768kbps_30fps_avc_stereo_48kHz_192kbps_aac.3gp",
670                             "bbb_cif_768kbps_30fps_avc_stereo_48kHz_192kbps_aac.mkv",}},
671                     {MediaFormat.MIMETYPE_AUDIO_AMR_NB, new String[]{
672                             "bbb_cif_768kbps_30fps_h263_mono_8kHz_12kbps_amrnb.3gp",
673                             "bbb_mono_8kHz_12kbps_amrnb.amr",}},
674                     {MediaFormat.MIMETYPE_AUDIO_AMR_WB, new String[]{
675                             "bbb_cif_768kbps_30fps_mpeg4_mono_16kHz_20kbps_amrwb.3gp",
676                             "bbb_mono_16kHz_20kbps_amrwb.amr"}},
677                     {MediaFormat.MIMETYPE_AUDIO_FLAC, new String[]{
678                             "bbb_cif_768kbps_30fps_mpeg4_stereo_48kHz_192kbps_flac.mp4",
679                             "bbb_cif_768kbps_30fps_h263_stereo_48kHz_192kbps_flac.mkv",}},
680             });
681         }
682 
683         private native boolean nativeTestExtract(String srcPath, String refPath, String mime);
684 
685         private native boolean nativeTestSeek(String srcPath, String mime);
686 
687         private native boolean nativeTestSeekFlakiness(String srcPath, String mime);
688 
689         private native boolean nativeTestSeekToZero(String srcPath, String mime);
690 
691         private native boolean nativeTestFileFormat(String srcPath);
692 
693         public FunctionalityTest(String mime, String[] srcFiles) {
694             mMime = mime;
695             mSrcFiles = srcFiles;
696         }
697 
698         // content necessary for testing seek are grouped in this class
699         private class SeekTestParams {
700             MediaCodec.BufferInfo mExpected;
701             long mTimeStamp;
702             int mMode;
703 
704             SeekTestParams(MediaCodec.BufferInfo expected, long timeStamp, int mode) {
705                 mExpected = expected;
706                 mTimeStamp = timeStamp;
707                 mMode = mode;
708             }
709         }
710 
711         private ArrayList<MediaCodec.BufferInfo> getSeekablePoints(String srcFile, String mime)
712                 throws IOException {
713             ArrayList<MediaCodec.BufferInfo> bookmarks = null;
714             if (mime == null) return null;
715             MediaExtractor extractor = new MediaExtractor();
716             extractor.setDataSource(mInpPrefix + srcFile);
717             for (int trackID = 0; trackID < extractor.getTrackCount(); trackID++) {
718                 MediaFormat format = extractor.getTrackFormat(trackID);
719                 if (!mime.equals(format.getString(MediaFormat.KEY_MIME))) continue;
720                 extractor.selectTrack(trackID);
721                 bookmarks = new ArrayList<>();
722                 do {
723                     int sampleFlags = extractor.getSampleFlags();
724                     if ((sampleFlags & MediaExtractor.SAMPLE_FLAG_SYNC) != 0) {
725                         MediaCodec.BufferInfo sampleInfo = new MediaCodec.BufferInfo();
726                         sampleInfo.set(0, (int) extractor.getSampleSize(),
727                                 extractor.getSampleTime(), extractor.getSampleFlags());
728                         bookmarks.add(sampleInfo);
729                     }
730                 } while (extractor.advance());
731                 extractor.unselectTrack(trackID);
732                 break;
733             }
734             extractor.release();
735             return bookmarks;
736         }
737 
738         private ArrayList<SeekTestParams> generateSeekTestArgs(String srcFile, String mime,
739                 boolean isRandom) throws IOException {
740             ArrayList<SeekTestParams> testArgs = new ArrayList<>();
741             if (mime == null) return null;
742             if (isRandom) {
743                 MediaExtractor extractor = new MediaExtractor();
744                 extractor.setDataSource(mInpPrefix + srcFile);
745                 final long maxEstDuration = 4000000;
746                 for (int trackID = 0; trackID < extractor.getTrackCount(); trackID++) {
747                     MediaFormat format = extractor.getTrackFormat(trackID);
748                     if (!mime.equals(format.getString(MediaFormat.KEY_MIME))) continue;
749                     extractor.selectTrack(trackID);
750                     for (int i = 0; i < MAX_SEEK_POINTS; i++) {
751                         long pts = (long) (mRandNum.nextDouble() * maxEstDuration);
752                         for (int mode = MediaExtractor.SEEK_TO_PREVIOUS_SYNC;
753                              mode <= MediaExtractor.SEEK_TO_CLOSEST_SYNC; mode++) {
754                             MediaCodec.BufferInfo currInfo = new MediaCodec.BufferInfo();
755                             extractor.seekTo(pts, mode);
756                             currInfo.set(0, (int) extractor.getSampleSize(),
757                                     extractor.getSampleTime(), extractor.getSampleFlags());
758                             testArgs.add(new SeekTestParams(currInfo, pts, mode));
759                         }
760                     }
761                     extractor.unselectTrack(trackID);
762                     break;
763                 }
764                 extractor.release();
765             } else {
766                 ArrayList<MediaCodec.BufferInfo> bookmarks = getSeekablePoints(srcFile, mime);
767                 if (bookmarks == null) return null;
768                 int size = bookmarks.size();
769                 int[] indices;
770                 if (size > MAX_SEEK_POINTS) {
771                     indices = new int[MAX_SEEK_POINTS];
772                     indices[0] = 0;
773                     indices[MAX_SEEK_POINTS - 1] = size - 1;
774                     for (int i = 1; i < MAX_SEEK_POINTS - 1; i++) {
775                         indices[i] = (int) (mRandNum.nextDouble() * (MAX_SEEK_POINTS - 1) + 1);
776                     }
777                 } else {
778                     indices = new int[size];
779                     for (int i = 0; i < size; i++) indices[i] = i;
780                 }
781                 // closest sync : Seek to the sync sample CLOSEST to the specified time
782                 // previous sync : Seek to a sync sample AT or AFTER the specified time
783                 // next sync : Seek to a sync sample AT or BEFORE the specified time
784                 for (int i : indices) {
785                     MediaCodec.BufferInfo currInfo = bookmarks.get(i);
786                     long pts = currInfo.presentationTimeUs;
787                     testArgs.add(
788                             new SeekTestParams(currInfo, pts, MediaExtractor.SEEK_TO_CLOSEST_SYNC));
789                     testArgs.add(
790                             new SeekTestParams(currInfo, pts, MediaExtractor.SEEK_TO_NEXT_SYNC));
791                     testArgs.add(
792                             new SeekTestParams(currInfo, pts,
793                                     MediaExtractor.SEEK_TO_PREVIOUS_SYNC));
794                     if (i > 0) {
795                         MediaCodec.BufferInfo prevInfo = bookmarks.get(i - 1);
796                         long ptsMinus = prevInfo.presentationTimeUs;
797                         ptsMinus = pts - ((pts - ptsMinus) >> 3);
798                         testArgs.add(new SeekTestParams(currInfo, ptsMinus,
799                                 MediaExtractor.SEEK_TO_CLOSEST_SYNC));
800                         testArgs.add(new SeekTestParams(currInfo, ptsMinus,
801                                 MediaExtractor.SEEK_TO_NEXT_SYNC));
802                         testArgs.add(new SeekTestParams(prevInfo, ptsMinus,
803                                 MediaExtractor.SEEK_TO_PREVIOUS_SYNC));
804                     }
805                     if (i < size - 1) {
806                         MediaCodec.BufferInfo nextInfo = bookmarks.get(i + 1);
807                         long ptsPlus = nextInfo.presentationTimeUs;
808                         ptsPlus = pts + ((ptsPlus - pts) >> 3);
809                         testArgs.add(new SeekTestParams(currInfo, ptsPlus,
810                                 MediaExtractor.SEEK_TO_CLOSEST_SYNC));
811                         testArgs.add(new SeekTestParams(nextInfo, ptsPlus,
812                                 MediaExtractor.SEEK_TO_NEXT_SYNC));
813                         testArgs.add(new SeekTestParams(currInfo, ptsPlus,
814                                 MediaExtractor.SEEK_TO_PREVIOUS_SYNC));
815                     }
816                 }
817             }
818             return testArgs;
819         }
820 
821         int checkSeekPoints(String srcFile, String mime,
822                 ArrayList<SeekTestParams> seekTestArgs) throws IOException {
823             int errCnt = 0;
824             MediaExtractor extractor = new MediaExtractor();
825             extractor.setDataSource(mInpPrefix + srcFile);
826             for (int trackID = 0; trackID < extractor.getTrackCount(); trackID++) {
827                 MediaFormat format = extractor.getTrackFormat(trackID);
828                 if (!format.getString(MediaFormat.KEY_MIME).equals(mime)) continue;
829                 extractor.selectTrack(trackID);
830                 MediaCodec.BufferInfo received = new MediaCodec.BufferInfo();
831                 for (SeekTestParams arg : seekTestArgs) {
832                     extractor.seekTo(arg.mTimeStamp, arg.mMode);
833                     received.set(0, (int) extractor.getSampleSize(), extractor.getSampleTime(),
834                             extractor.getSampleFlags());
835                     if (!isSampleInfoIdentical(arg.mExpected, received)) {
836                         errCnt++;
837                         if (ENABLE_LOGS) {
838                             Log.d(LOG_TAG, " flags exp/got: " + arg.mExpected.flags + '/' +
839                                     received.flags);
840                             Log.d(LOG_TAG,
841                                     " size exp/got: " + arg.mExpected.size + '/' + received.size);
842                             Log.d(LOG_TAG,
843                                     " ts exp/got: " + arg.mExpected.presentationTimeUs + '/' +
844                                             received.presentationTimeUs);
845                         }
846                     }
847                 }
848                 extractor.unselectTrack(trackID);
849                 break;
850             }
851             extractor.release();
852             return errCnt;
853         }
854 
855         /**
856          * Audio, Video codecs support a variety of file-types/container formats. For example,
857          * Vorbis supports OGG, MP4, WEBM and MKV. H.263 supports 3GPP, WEBM and MKV. For every
858          * mime, a list of test vectors are provided one for each container) but underlying
859          * elementary stream is the same for all. The streams of a mime are extracted and
860          * compared with each other for similarity.
861          */
862         @LargeTest
863         @Test
864         public void testExtract() throws IOException {
865             assumeTrue(shouldRunTest(mMime));
866             assertTrue(mSrcFiles.length > 1);
867             assumeTrue("TODO(b/146925481)", !mMime.equals(MediaFormat.MIMETYPE_AUDIO_VORBIS));
868             assumeTrue("TODO(b/146925481)", !mMime.equals(MediaFormat.MIMETYPE_AUDIO_OPUS));
869             assumeTrue("TODO(b/146925481)", !mMime.equals(MediaFormat.MIMETYPE_AUDIO_MPEG));
870             assumeTrue("TODO(b/146925481)", !mMime.equals(MediaFormat.MIMETYPE_AUDIO_AAC));
871             MediaExtractor refExtractor = new MediaExtractor();
872             refExtractor.setDataSource(mInpPrefix + mSrcFiles[0]);
873             boolean isOk = true;
874             for (int i = 1; i < mSrcFiles.length && isOk; i++) {
875                 MediaExtractor testExtractor = new MediaExtractor();
876                 testExtractor.setDataSource(mInpPrefix + mSrcFiles[i]);
877                 if (!isMediaSimilar(refExtractor, testExtractor, mMime, Integer.MAX_VALUE)) {
878                     if (ENABLE_LOGS) {
879                         Log.d(LOG_TAG, "Files: " + mSrcFiles[0] + ", " + mSrcFiles[i] +
880                                 " are different from extractor perspective");
881                     }
882                     if (!codecListSupp.contains(mMime)) {
883                         isOk = false;
884                     }
885                 }
886                 testExtractor.release();
887             }
888             refExtractor.release();
889             assertTrue(testName.getMethodName() + " failed for mime: " + mMime, isOk);
890         }
891 
892         /**
893          * Tests seek functionality, verifies if we seek to most accurate point for a given
894          * choice of timestamp and mode.
895          */
896         @LargeTest
897         @Test
898         @Ignore("TODO(b/146420831)")
899         public void testSeek() throws IOException {
900             assumeTrue(shouldRunTest(mMime));
901             boolean isOk = true;
902             for (String srcFile : mSrcFiles) {
903                 ArrayList<SeekTestParams> seekTestArgs =
904                         generateSeekTestArgs(srcFile, mMime, false);
905                 assertTrue("Mime is null.", seekTestArgs != null);
906                 assertTrue("No sync samples found.", !seekTestArgs.isEmpty());
907                 Collections.shuffle(seekTestArgs, mRandNum);
908                 int seekAccErrCnt = checkSeekPoints(srcFile, mMime, seekTestArgs);
909                 if (seekAccErrCnt != 0) {
910                     if (ENABLE_LOGS) {
911                         Log.d(LOG_TAG, "For " + srcFile + " seek chose inaccurate Sync point in: " +
912                                 seekAccErrCnt + "/" + seekTestArgs.size());
913                     }
914                     if (!codecListSupp.contains(mMime)) {
915                         isOk = false;
916                         break;
917                     }
918                 }
919             }
920             assertTrue(testName.getMethodName() + " failed for mime: " + mMime, isOk);
921         }
922 
923         /**
924          * Tests if we get the same content each time after a call to seekto;
925          */
926         @LargeTest
927         @Test
928         public void testSeekFlakiness() throws IOException {
929             assumeTrue(shouldRunTest(mMime));
930             boolean isOk = true;
931             for (String srcFile : mSrcFiles) {
932                 ArrayList<SeekTestParams> seekTestArgs = generateSeekTestArgs(srcFile, mMime, true);
933                 assertTrue("Mime is null.", seekTestArgs != null);
934                 assertTrue("No samples found.", !seekTestArgs.isEmpty());
935                 Collections.shuffle(seekTestArgs, mRandNum);
936                 int flakyErrCnt = checkSeekPoints(srcFile, mMime, seekTestArgs);
937                 if (flakyErrCnt != 0) {
938                     if (ENABLE_LOGS) {
939                         Log.d(LOG_TAG,
940                                 "No. of Samples where seek showed flakiness is: " + flakyErrCnt);
941                     }
942                     if (!codecListSupp.contains(mMime)) {
943                         isOk = false;
944                         break;
945                     }
946                 }
947             }
948             assertTrue(testName.getMethodName() + " failed for mime: " + mMime, isOk);
949         }
950 
951         /**
952          * Test if seekTo(0) yields the same content as if we had just opened the file and started
953          * reading.
954          */
955         @SmallTest
956         @Test
957         public void testSeekToZero() throws IOException {
958             assumeTrue(shouldRunTest(mMime));
959             assumeTrue("TODO(b/146925481)", !mMime.equals(MediaFormat.MIMETYPE_AUDIO_VORBIS));
960             assumeTrue("TODO(b/146925481)", !mMime.equals(MediaFormat.MIMETYPE_AUDIO_MPEG));
961             assumeTrue("TODO(b/146925481)", !mMime.equals(MediaFormat.MIMETYPE_AUDIO_AAC));
962             boolean isOk = true;
963             for (String srcFile : mSrcFiles) {
964                 MediaExtractor extractor = new MediaExtractor();
965                 extractor.setDataSource(mInpPrefix + srcFile);
966                 MediaCodec.BufferInfo sampleInfoAtZero = new MediaCodec.BufferInfo();
967                 MediaCodec.BufferInfo currInfo = new MediaCodec.BufferInfo();
968                 final long randomSeekPts = 1 << 20;
969                 for (int trackID = 0; trackID < extractor.getTrackCount(); trackID++) {
970                     MediaFormat format = extractor.getTrackFormat(trackID);
971                     if (!mMime.equals(format.getString(MediaFormat.KEY_MIME))) continue;
972                     extractor.selectTrack(trackID);
973                     sampleInfoAtZero.set(0, (int) extractor.getSampleSize(),
974                             extractor.getSampleTime(), extractor.getSampleFlags());
975                     extractor.seekTo(randomSeekPts, MediaExtractor.SEEK_TO_NEXT_SYNC);
976                     extractor.seekTo(0, MediaExtractor.SEEK_TO_CLOSEST_SYNC);
977                     currInfo.set(0, (int) extractor.getSampleSize(),
978                             extractor.getSampleTime(), extractor.getSampleFlags());
979                     if (!isSampleInfoIdentical(sampleInfoAtZero, currInfo)) {
980                         if (!codecListSupp.contains(mMime)) {
981                             if (ENABLE_LOGS) {
982                                 Log.d(LOG_TAG, "seen mismatch seekTo(0, SEEK_TO_CLOSEST_SYNC)");
983                                 Log.d(LOG_TAG, " flags exp/got: " + sampleInfoAtZero.flags + '/' +
984                                         currInfo.flags);
985                                 Log.d(LOG_TAG, " size exp/got: " + sampleInfoAtZero.size + '/' +
986                                         currInfo.size);
987                                 Log.d(LOG_TAG,
988                                         " ts exp/got: " + sampleInfoAtZero.presentationTimeUs +
989                                                 '/' + currInfo.presentationTimeUs);
990                             }
991                             isOk = false;
992                             break;
993                         }
994                     }
995                     extractor.seekTo(-1L, MediaExtractor.SEEK_TO_CLOSEST_SYNC);
996                     currInfo.set(0, (int) extractor.getSampleSize(),
997                             extractor.getSampleTime(), extractor.getSampleFlags());
998                     if (!isSampleInfoIdentical(sampleInfoAtZero, currInfo)) {
999                         if (!codecListSupp.contains(mMime)) {
1000                             if (ENABLE_LOGS) {
1001                                 Log.d(LOG_TAG, "seen mismatch seekTo(-1, SEEK_TO_CLOSEST_SYNC)");
1002                                 Log.d(LOG_TAG, " flags exp/got: " + sampleInfoAtZero.flags + '/' +
1003                                         currInfo.flags);
1004                                 Log.d(LOG_TAG, " size exp/got: " + sampleInfoAtZero.size + '/' +
1005                                         currInfo.size);
1006                                 Log.d(LOG_TAG,
1007                                         " ts exp/got: " + sampleInfoAtZero.presentationTimeUs +
1008                                                 '/' + currInfo.presentationTimeUs);
1009                             }
1010                             isOk = false;
1011                             break;
1012                         }
1013                     }
1014                     extractor.unselectTrack(trackID);
1015                 }
1016                 extractor.release();
1017             }
1018             assertTrue(testName.getMethodName() + " failed for mime: " + mMime, isOk);
1019         }
1020 
1021         @SmallTest
1022         @Test
1023         public void testMetrics() throws IOException {
1024             assumeTrue(shouldRunTest(mMime));
1025             for (String srcFile : mSrcFiles) {
1026                 MediaExtractor extractor = new MediaExtractor();
1027                 extractor.setDataSource(mInpPrefix + srcFile);
1028                 PersistableBundle bundle = extractor.getMetrics();
1029                 int numTracks = bundle.getInt(MediaExtractor.MetricsConstants.TRACKS);
1030                 String format = bundle.getString(MediaExtractor.MetricsConstants.FORMAT);
1031                 String mime = bundle.getString(MediaExtractor.MetricsConstants.MIME_TYPE);
1032                 assertTrue(numTracks == extractor.getTrackCount() && format != null &&
1033                         mime != null);
1034                 extractor.release();
1035             }
1036         }
1037 
1038         @LargeTest
1039         @Test
1040         public void testExtractNative() {
1041             assumeTrue(shouldRunTest(mMime));
1042             assertTrue(mSrcFiles.length > 1);
1043             assumeTrue("TODO(b/146925481)", !mMime.equals(MediaFormat.MIMETYPE_AUDIO_VORBIS));
1044             assumeTrue("TODO(b/146925481)", !mMime.equals(MediaFormat.MIMETYPE_AUDIO_OPUS));
1045             assumeTrue("TODO(b/146925481)", !mMime.equals(MediaFormat.MIMETYPE_AUDIO_MPEG));
1046             assumeTrue("TODO(b/146925481)", !mMime.equals(MediaFormat.MIMETYPE_AUDIO_AAC));
1047             boolean isOk = true;
1048             for (int i = 1; i < mSrcFiles.length; i++) {
1049                 if (!nativeTestExtract(mInpPrefix + mSrcFiles[0], mInpPrefix + mSrcFiles[i],
1050                         mMime)) {
1051                     Log.d(LOG_TAG, "Files: " + mSrcFiles[0] + ", " + mSrcFiles[i] +
1052                             " are different from extractor perpsective");
1053                     if (!codecListSupp.contains(mMime)) {
1054                         isOk = false;
1055                         break;
1056                     }
1057                 }
1058             }
1059             assertTrue(testName.getMethodName() + " failed for mime: " + mMime, isOk);
1060         }
1061 
1062         @LargeTest
1063         @Test
1064         @Ignore("TODO(b/146420831)")
1065         public void testSeekNative() {
1066             assumeTrue(shouldRunTest(mMime));
1067             boolean isOk = true;
1068             for (String srcFile : mSrcFiles) {
1069                 if (!nativeTestSeek(mInpPrefix + srcFile, mMime)) {
1070                     if (!codecListSupp.contains(mMime)) {
1071                         isOk = false;
1072                         break;
1073                     }
1074                 }
1075             }
1076             assertTrue(testName.getMethodName() + " failed for mime: " + mMime, isOk);
1077         }
1078 
1079         @LargeTest
1080         @Test
1081         public void testSeekFlakinessNative() {
1082             assumeTrue(shouldRunTest(mMime));
1083             boolean isOk = true;
1084             for (String srcFile : mSrcFiles) {
1085                 if (!nativeTestSeekFlakiness(mInpPrefix + srcFile, mMime)) {
1086                     if (!codecListSupp.contains(mMime)) {
1087                         isOk = false;
1088                         break;
1089                     }
1090                 }
1091             }
1092             assertTrue(testName.getMethodName() + " failed for mime: " + mMime, isOk);
1093         }
1094 
1095         @SmallTest
1096         @Test
1097         public void testSeekToZeroNative() {
1098             assumeTrue(shouldRunTest(mMime));
1099             assumeTrue("TODO(b/146925481)", !mMime.equals(MediaFormat.MIMETYPE_AUDIO_VORBIS));
1100             assumeTrue("TODO(b/146925481)", !mMime.equals(MediaFormat.MIMETYPE_AUDIO_MPEG));
1101             assumeTrue("TODO(b/146925481)", !mMime.equals(MediaFormat.MIMETYPE_AUDIO_AAC));
1102             boolean isOk = true;
1103             for (String srcFile : mSrcFiles) {
1104                 if (!nativeTestSeekToZero(mInpPrefix + srcFile, mMime)) {
1105                     if (!codecListSupp.contains(mMime)) {
1106                         isOk = false;
1107                         break;
1108                     }
1109                 }
1110             }
1111             assertTrue(testName.getMethodName() + " failed for mime: " + mMime, isOk);
1112         }
1113 
1114         @SmallTest
1115         @Test
1116         public void testFileFormatNative() {
1117             assumeTrue(shouldRunTest(mMime));
1118             boolean isOk = true;
1119             for (String srcFile : mSrcFiles) {
1120                 if (!nativeTestFileFormat(mInpPrefix + srcFile)) {
1121                     isOk = false;
1122                     break;
1123                 }
1124                 assertTrue(testName.getMethodName() + " failed for mime: " + mMime, isOk);
1125             }
1126         }
1127     }
1128 }
1129