xref: /aosp_15_r20/cts/tests/tests/mediaparser/src/android/media/mediaparser/cts/MediaParserTest.java (revision b7c941bb3fa97aba169d73cee0bed2de8ac964bf)
1 /*
2  * Copyright 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.media.mediaparser.cts;
18 
19 import static com.google.common.truth.Truth.assertThat;
20 
21 import static org.junit.Assert.fail;
22 import static org.junit.Assume.assumeTrue;
23 
24 import android.media.MediaCodec;
25 import android.media.MediaFormat;
26 import android.media.MediaParser;
27 import android.media.metrics.LogSessionId;
28 import android.media.metrics.MediaMetricsManager;
29 import android.media.metrics.PlaybackSession;
30 import android.os.Build;
31 
32 import androidx.test.ext.junit.runners.AndroidJUnit4;
33 import androidx.test.platform.app.InstrumentationRegistry;
34 
35 import com.android.compatibility.common.util.ModuleSpecificTest;
36 
37 import com.google.android.exoplayer2.testutil.FakeExtractorInput;
38 import com.google.android.exoplayer2.testutil.TestUtil;
39 import com.google.android.exoplayer2.util.Util;
40 
41 import org.junit.Before;
42 import org.junit.Ignore;
43 import org.junit.Test;
44 import org.junit.runner.RunWith;
45 
46 import java.io.IOException;
47 import java.util.Arrays;
48 import java.util.Collections;
49 import java.util.Map;
50 
51 @RunWith(AndroidJUnit4.class)
52 @ModuleSpecificTest
53 public class MediaParserTest {
54 
55     private static final String PARAMETER_IN_BAND_CRYPTO_INFO =
56             "android.media.mediaparser.inBandCryptoInfo";
57 
58     @Before
setUp()59     public void setUp() {
60         assumeTrue(Build.VERSION.SDK_INT >= Build.VERSION_CODES.R);
61     }
62 
63     @Test
testDefaultLogSessionId()64     public void testDefaultLogSessionId() {
65         assumeTrue(Build.VERSION.SDK_INT >= Build.VERSION_CODES.S);
66         MediaParser mediaParser = MediaParser.create(new MockMediaParserOutputConsumer());
67         assertThat(mediaParser.getLogSessionId())
68                 .isSameInstanceAs(LogSessionId.LOG_SESSION_ID_NONE);
69         mediaParser.release();
70     }
71 
72     @Test
testSetLogSessionId()73     public void testSetLogSessionId() {
74         assumeTrue(Build.VERSION.SDK_INT >= Build.VERSION_CODES.S);
75         MediaParser mediaParser = MediaParser.create(new MockMediaParserOutputConsumer());
76         MediaMetricsManager mediaMetricsManager =
77                 InstrumentationRegistry.getInstrumentation()
78                         .getTargetContext()
79                         .getSystemService(MediaMetricsManager.class);
80         PlaybackSession playbackSession = mediaMetricsManager.createPlaybackSession();
81         LogSessionId logSessionId = playbackSession.getSessionId();
82         mediaParser.setLogSessionId(logSessionId);
83         assertThat(mediaParser.getLogSessionId()).isSameInstanceAs(logSessionId);
84         mediaParser.release();
85     }
86 
87     @Test
testGetAllParserNames()88     public void testGetAllParserNames() {
89         MediaFormat format = new MediaFormat();
90         // By not providing a mime type, MediaParser should return all parser names.
91         format.setString(MediaFormat.KEY_MIME, null);
92         assertThat(MediaParser.getParserNames(format))
93                 .containsExactly(
94                         MediaParser.PARSER_NAME_MATROSKA,
95                         MediaParser.PARSER_NAME_FMP4,
96                         MediaParser.PARSER_NAME_MP4,
97                         MediaParser.PARSER_NAME_MP3,
98                         MediaParser.PARSER_NAME_ADTS,
99                         MediaParser.PARSER_NAME_AC3,
100                         MediaParser.PARSER_NAME_TS,
101                         MediaParser.PARSER_NAME_FLV,
102                         MediaParser.PARSER_NAME_OGG,
103                         MediaParser.PARSER_NAME_PS,
104                         MediaParser.PARSER_NAME_WAV,
105                         MediaParser.PARSER_NAME_AMR,
106                         MediaParser.PARSER_NAME_AC4,
107                         MediaParser.PARSER_NAME_FLAC);
108     }
109 
110     @Test
testGetParserNamesByMimeType()111     public void testGetParserNamesByMimeType() {
112         // MimeTypes obtained from the W3C.
113         assertParsers(MediaParser.PARSER_NAME_MATROSKA)
114                 .supportMimeTypes(
115                         "video/x-matroska", "audio/x-matroska", "video/x-webm", "audio/x-webm");
116         assertParsers(MediaParser.PARSER_NAME_MP4, MediaParser.PARSER_NAME_FMP4)
117                 .supportMimeTypes("video/mp4", "audio/mp4", "application/mp4");
118         assertParsers(MediaParser.PARSER_NAME_MP3).supportMimeTypes("audio/mpeg");
119         assertParsers(MediaParser.PARSER_NAME_ADTS).supportMimeTypes("audio/aac");
120         assertParsers(MediaParser.PARSER_NAME_AC3).supportMimeTypes("audio/ac3");
121         assertParsers(MediaParser.PARSER_NAME_TS).supportMimeTypes("video/mp2t", "audio/mp2t");
122         assertParsers(MediaParser.PARSER_NAME_FLV).supportMimeTypes("video/x-flv");
123         assertParsers(MediaParser.PARSER_NAME_OGG)
124                 .supportMimeTypes("video/ogg", "audio/ogg", "application/ogg");
125         assertParsers(MediaParser.PARSER_NAME_PS).supportMimeTypes("video/mp2p", "video/mp1s");
126         assertParsers(MediaParser.PARSER_NAME_WAV)
127                 .supportMimeTypes("audio/vnd.wave", "audio/wav", "audio/wave", "audio/x-wav");
128         assertParsers(MediaParser.PARSER_NAME_AMR).supportMimeTypes("audio/amr");
129         assertParsers(MediaParser.PARSER_NAME_AC4).supportMimeTypes("audio/ac4");
130         assertParsers(MediaParser.PARSER_NAME_FLAC).supportMimeTypes("audio/flac", "audio/x-flac");
131     }
132 
133     @Test
testGetParserNamesForUnsupportedMimeType()134     public void testGetParserNamesForUnsupportedMimeType() {
135         MediaFormat format = new MediaFormat();
136         // None of the parser supports WebVTT.
137         format.setString(MediaFormat.KEY_MIME, "text/vtt");
138         assertThat(MediaParser.getParserNames(format)).isEmpty();
139     }
140 
141     @Test
testCreationByName()142     public void testCreationByName() {
143         testCreationByName(MediaParser.PARSER_NAME_MATROSKA);
144         testCreationByName(MediaParser.PARSER_NAME_FMP4);
145         testCreationByName(MediaParser.PARSER_NAME_MP4);
146         testCreationByName(MediaParser.PARSER_NAME_MP3);
147         testCreationByName(MediaParser.PARSER_NAME_ADTS);
148         testCreationByName(MediaParser.PARSER_NAME_AC3);
149         testCreationByName(MediaParser.PARSER_NAME_TS);
150         testCreationByName(MediaParser.PARSER_NAME_FLV);
151         testCreationByName(MediaParser.PARSER_NAME_OGG);
152         testCreationByName(MediaParser.PARSER_NAME_PS);
153         testCreationByName(MediaParser.PARSER_NAME_WAV);
154         testCreationByName(MediaParser.PARSER_NAME_AMR);
155         testCreationByName(MediaParser.PARSER_NAME_AC4);
156         testCreationByName(MediaParser.PARSER_NAME_FLAC);
157         try {
158             testCreationByName("android.media.mediaparser.ExtractorThatDoesNotExist");
159             fail();
160         } catch (IllegalArgumentException e) {
161             // Expected.
162         }
163     }
164 
165     @Test
testSupportsParameter()166     public void testSupportsParameter() {
167         assertSupportFor(MediaParser.PARAMETER_ADTS_ENABLE_CBR_SEEKING);
168         assertSupportFor(MediaParser.PARAMETER_AMR_ENABLE_CBR_SEEKING);
169         assertSupportFor(MediaParser.PARAMETER_FLAC_DISABLE_ID3);
170         assertSupportFor(MediaParser.PARAMETER_MP4_IGNORE_EDIT_LISTS);
171         assertSupportFor(MediaParser.PARAMETER_MP4_IGNORE_TFDT_BOX);
172         assertSupportFor(MediaParser.PARAMETER_MP4_TREAT_VIDEO_FRAMES_AS_KEYFRAMES);
173         assertSupportFor(MediaParser.PARAMETER_MATROSKA_DISABLE_CUES_SEEKING);
174         assertSupportFor(MediaParser.PARAMETER_MP3_DISABLE_ID3);
175         assertSupportFor(MediaParser.PARAMETER_MP3_ENABLE_CBR_SEEKING);
176         assertSupportFor(MediaParser.PARAMETER_MP3_ENABLE_INDEX_SEEKING);
177         assertSupportFor(MediaParser.PARAMETER_TS_MODE);
178         assertSupportFor(MediaParser.PARAMETER_TS_ALLOW_NON_IDR_AVC_KEYFRAMES);
179         assertSupportFor(MediaParser.PARAMETER_TS_IGNORE_AAC_STREAM);
180         assertSupportFor(MediaParser.PARAMETER_TS_IGNORE_AVC_STREAM);
181         assertSupportFor(MediaParser.PARAMETER_TS_IGNORE_SPLICE_INFO_STREAM);
182         assertSupportFor(MediaParser.PARAMETER_TS_DETECT_ACCESS_UNITS);
183         assertSupportFor(MediaParser.PARAMETER_TS_ENABLE_HDMV_DTS_AUDIO_STREAMS);
184     }
185 
186     @Test
testSetKnownParameters()187     public void testSetKnownParameters() {
188         testValidAndInvalidValueForBooleanParameter(MediaParser.PARAMETER_ADTS_ENABLE_CBR_SEEKING);
189         testValidAndInvalidValueForBooleanParameter(MediaParser.PARAMETER_AMR_ENABLE_CBR_SEEKING);
190         testValidAndInvalidValueForBooleanParameter(MediaParser.PARAMETER_FLAC_DISABLE_ID3);
191         testValidAndInvalidValueForBooleanParameter(MediaParser.PARAMETER_MP4_IGNORE_EDIT_LISTS);
192         testValidAndInvalidValueForBooleanParameter(MediaParser.PARAMETER_MP4_IGNORE_TFDT_BOX);
193         testValidAndInvalidValueForBooleanParameter(
194                 MediaParser.PARAMETER_MP4_TREAT_VIDEO_FRAMES_AS_KEYFRAMES);
195         testValidAndInvalidValueForBooleanParameter(
196                 MediaParser.PARAMETER_MATROSKA_DISABLE_CUES_SEEKING);
197         testValidAndInvalidValueForBooleanParameter(MediaParser.PARAMETER_MP3_DISABLE_ID3);
198         testValidAndInvalidValueForBooleanParameter(MediaParser.PARAMETER_MP3_ENABLE_CBR_SEEKING);
199         testValidAndInvalidValueForBooleanParameter(MediaParser.PARAMETER_MP3_ENABLE_INDEX_SEEKING);
200         testValidAndInvalidValueForBooleanParameter(
201                 MediaParser.PARAMETER_TS_ALLOW_NON_IDR_AVC_KEYFRAMES);
202         testValidAndInvalidValueForBooleanParameter(MediaParser.PARAMETER_TS_IGNORE_AAC_STREAM);
203         testValidAndInvalidValueForBooleanParameter(MediaParser.PARAMETER_TS_IGNORE_AVC_STREAM);
204         testValidAndInvalidValueForBooleanParameter(
205                 MediaParser.PARAMETER_TS_IGNORE_SPLICE_INFO_STREAM);
206         testValidAndInvalidValueForBooleanParameter(MediaParser.PARAMETER_TS_DETECT_ACCESS_UNITS);
207         testValidAndInvalidValueForBooleanParameter(
208                 MediaParser.PARAMETER_TS_ENABLE_HDMV_DTS_AUDIO_STREAMS);
209         testParameterSetting(
210                 MediaParser.PARAMETER_TS_MODE, /* value= */ 1, /* valueIsIllegal= */ true);
211         testParameterSetting(
212                 MediaParser.PARAMETER_TS_MODE,
213                 /* value= */ "invalid_mode",
214                 /* valueIsIllegal= */ true);
215         testParameterSetting(
216                 MediaParser.PARAMETER_TS_MODE,
217                 /* value= */ "single_pmt",
218                 /* valueIsIllegal= */ false);
219         testParameterSetting(
220                 MediaParser.PARAMETER_TS_MODE, /* value= */ "hls", /* valueIsIllegal= */ false);
221         testParameterSetting(
222                 MediaParser.PARAMETER_TS_MODE,
223                 /* value= */ "multi_pmt",
224                 /* valueIsIllegal= */ false);
225     }
226 
227     @Test
testSetUnknownParameter()228     public void testSetUnknownParameter() {
229         String parameterName = "android.media.mediaparser.unsupportedParameterName";
230         // All of the following should be ignored.
231         testParameterSetting(parameterName, /* value= */ 1, /* valueIsIllegal= */ false);
232         testParameterSetting(parameterName, /* value= */ "string", /* valueIsIllegal= */ false);
233         testParameterSetting(parameterName, /* value= */ true, /* valueIsIllegal= */ false);
234     }
235 
236     @Test
testLackOfSupportForUnsupportedParameter()237     public void testLackOfSupportForUnsupportedParameter() {
238         MediaParser mediaParser = MediaParser.create(new MockMediaParserOutputConsumer());
239         assertThat(mediaParser.supportsParameter("android.media.mediaparser.UNSUPPORTED_PARAMETER"))
240                 .isFalse();
241         mediaParser.release();
242     }
243 
244     // OGG.
245 
246     @Test
testOggBearVorbis()247     public void testOggBearVorbis() throws IOException {
248         testAssetExtraction("media/ogg/bear_vorbis.ogg");
249     }
250 
251     @Test
testOggBear()252     public void testOggBear() throws IOException {
253         testAssetExtraction("media/ogg/bear.opus");
254     }
255 
256     @Test
testOggBearFlac()257     public void testOggBearFlac() throws IOException {
258         testAssetExtraction("media/ogg/bear_flac.ogg");
259     }
260 
261     @Test
testOggNoFlacSeekTable()262     public void testOggNoFlacSeekTable() throws IOException {
263         testAssetExtraction("media/ogg/bear_flac_noseektable.ogg");
264     }
265 
266     @Test
testOggFlacHeaderSniff()267     public void testOggFlacHeaderSniff() throws IOException {
268         testSniffAsset(
269                 "media/ogg/flac_header", /* expectedExtractorName= */ MediaParser.PARSER_NAME_OGG);
270     }
271 
272     @Test
testOggOpusHeaderSniff()273     public void testOggOpusHeaderSniff() throws IOException {
274         try {
275             testSniffAsset(
276                     "media/ogg/opus_header",
277                     /* expectedExtractorName= */ MediaParser.PARSER_NAME_OGG);
278             fail();
279         } catch (MediaParser.UnrecognizedInputFormatException e) {
280             // Expected.
281         }
282     }
283 
284     @Test
testOggInvalidHeaderSniff()285     public void testOggInvalidHeaderSniff() throws IOException {
286         try {
287             testSniffAsset(
288                     "media/ogg/invalid_ogg_header",
289                     /* expectedExtractorName= */ MediaParser.PARSER_NAME_OGG);
290             fail();
291         } catch (MediaParser.UnrecognizedInputFormatException e) {
292             // Expected.
293         }
294         try {
295             testSniffAsset(
296                     "media/ogg/invalid_header",
297                     /* expectedExtractorName= */ MediaParser.PARSER_NAME_OGG);
298             fail();
299         } catch (MediaParser.UnrecognizedInputFormatException e) {
300             // Expected.
301         }
302     }
303 
304     // FLAC.
305 
306     @Test
testFlacUncommonSampleRateFlac()307     public void testFlacUncommonSampleRateFlac() throws IOException {
308         testAssetExtraction("media/flac/bear_uncommon_sample_rate.flac");
309     }
310 
311     @Test
testFlacNoSeekTableAndNoNumSamples()312     public void testFlacNoSeekTableAndNoNumSamples() throws IOException {
313         testAssetExtraction("media/flac/bear_no_seek_table_no_num_samples.flac");
314     }
315 
316     @Test
testFlacWithPicture()317     public void testFlacWithPicture() throws IOException {
318         testAssetExtraction("media/flac/bear_with_picture.flac");
319     }
320 
321     @Test
testFlacWithVorbisComments()322     public void testFlacWithVorbisComments() throws IOException {
323         testAssetExtraction("media/flac/bear_with_vorbis_comments.flac");
324     }
325 
326     @Test
testFlacOneMetadataBlock()327     public void testFlacOneMetadataBlock() throws IOException {
328         testAssetExtraction("media/flac/bear_one_metadata_block.flac");
329     }
330 
331     @Test
testFlacNoMinMaxFrameSize()332     public void testFlacNoMinMaxFrameSize() throws IOException {
333         testAssetExtraction("media/flac/bear_no_min_max_frame_size.flac");
334     }
335 
336     @Test
testFlacNoNumSamples()337     public void testFlacNoNumSamples() throws IOException {
338         testAssetExtraction("media/flac/bear_no_num_samples.flac");
339     }
340 
341     @Test
testFlacWithId3()342     public void testFlacWithId3() throws IOException {
343         testAssetExtraction("media/flac/bear_with_id3.flac");
344     }
345 
346     @Test
testFlacSample()347     public void testFlacSample() throws IOException {
348         testAssetExtraction("media/flac/bear.flac");
349     }
350 
351     // MP3.
352 
353     @Test
testMp3WithNoSeekTableVariableFrameSize()354     public void testMp3WithNoSeekTableVariableFrameSize() throws IOException {
355         testAssetExtraction("media/mp3/bear-cbr-variable-frame-size-no-seek-table.mp3");
356     }
357 
358     @Test
testMp3WithVariableBitrateAndXingHeader()359     public void testMp3WithVariableBitrateAndXingHeader() throws IOException {
360         testAssetExtraction("media/mp3/bear-vbr-xing-header.mp3");
361     }
362 
363     @Test
testMp3WithNoSeekTableVariableBitrate()364     public void testMp3WithNoSeekTableVariableBitrate() throws IOException {
365         testAssetExtraction("media/mp3/bear-vbr-no-seek-table.mp3");
366     }
367 
368     @Test
testMp3WithTrimmedSample()369     public void testMp3WithTrimmedSample() throws IOException {
370         testAssetExtraction("media/mp3/play-trimmed.mp3");
371     }
372 
373     @Test
testMp3WithId3()374     public void testMp3WithId3() throws IOException {
375         testAssetExtraction("media/mp3/bear-id3.mp3");
376     }
377 
378     // WAV.
379 
380     @Test
testWavWithImaAdpcm()381     public void testWavWithImaAdpcm() throws IOException {
382         testAssetExtraction("media/wav/sample_ima_adpcm.wav");
383     }
384 
385     @Test
testWav()386     public void testWav() throws IOException {
387         testAssetExtraction("media/wav/sample.wav");
388     }
389 
390     // AMR.
391 
392     @Test
testAmrNarrowBandSamplesWithConstantBitrateSeeking()393     public void testAmrNarrowBandSamplesWithConstantBitrateSeeking() throws IOException {
394         testAssetExtraction("media/amr/sample_nb_cbr.amr");
395     }
396 
397     @Test
testAmrNarrowBandSamples()398     public void testAmrNarrowBandSamples() throws IOException {
399         testAssetExtraction("media/amr/sample_nb.amr");
400     }
401 
402     @Test
testAmrWideBandSamples()403     public void testAmrWideBandSamples() throws IOException {
404         testAssetExtraction("media/amr/sample_wb.amr");
405     }
406 
407     @Test
testAmrWideBandSamplesWithConstantBitrateSeeking()408     public void testAmrWideBandSamplesWithConstantBitrateSeeking() throws IOException {
409         testAssetExtraction("media/amr/sample_wb_cbr.amr");
410     }
411 
412     // FLV.
413 
414     @Test
testFlv()415     public void testFlv() throws IOException {
416         testAssetExtraction("media/flv/sample.flv");
417     }
418 
419     // PS.
420 
421     // TODO: Enable once the timeout is fixed.
422     @Test
423     @Ignore
testPsElphantsDream()424     public void testPsElphantsDream() throws IOException {
425         testAssetExtraction("media/ts/elephants_dream.mpg");
426     }
427 
428     @Test
testPsWithAc3()429     public void testPsWithAc3() throws IOException {
430         testAssetExtraction("media/ts/sample_ac3.ps");
431     }
432 
433     @Test
testPsWithH262MpegAudio()434     public void testPsWithH262MpegAudio() throws IOException {
435         testAssetExtraction("media/ts/sample_h262_mpeg_audio.ps");
436     }
437 
438     // ADTS.
439 
440     @Test
testAdtsTruncatedWithConstantBitrateSeeking()441     public void testAdtsTruncatedWithConstantBitrateSeeking() throws IOException {
442         testAssetExtraction(
443                 "media/ts/sample_cbs_truncated.adts",
444                 Collections.singletonMap(MediaParser.PARAMETER_ADTS_ENABLE_CBR_SEEKING, true));
445     }
446 
447     @Test
testAdts()448     public void testAdts() throws IOException {
449         testAssetExtraction("media/ts/sample.adts");
450     }
451 
452     // AC-3.
453 
454     @Test
testAc3()455     public void testAc3() throws IOException {
456         testAssetExtraction("media/ts/sample.ac3");
457     }
458 
459     // AC-4.
460 
461     @Test
testAc4()462     public void testAc4() throws IOException {
463         testAssetExtraction("media/ts/sample.ac4");
464     }
465 
466     // EAC-3.
467 
468     @Test
testEac3()469     public void testEac3() throws IOException {
470         testAssetExtraction("media/ts/sample.eac3");
471     }
472 
473     // TS.
474 
475     @Test
testTsBigBuckBunny()476     public void testTsBigBuckBunny() throws IOException {
477         // This file is too big to run a full extraction with multiple seeks like other tests do.
478         MockMediaParserOutputConsumer outputConsumer = new MockMediaParserOutputConsumer();
479         MockMediaParserInputReader inputReader = getInputReader("media/ts/bbb_2500ms.ts");
480         MediaParser mediaParser = MediaParser.create(outputConsumer);
481         advanceUntilSample(outputConsumer, inputReader, mediaParser, /* sampleNumber= */ 100);
482         assertThat(outputConsumer.getSeekMap().getDurationMicros()).isEqualTo(2_500_000);
483         mediaParser.seek(MediaParser.SeekPoint.START);
484         inputReader.setPosition(0);
485         advanceUntilSample(outputConsumer, inputReader, mediaParser, /* sampleNumber= */ 101);
486     }
487 
488     @Test
testTsWithH262MpegAudio()489     public void testTsWithH262MpegAudio() throws IOException {
490         testAssetExtraction("media/ts/sample_h262_mpeg_audio.ts");
491     }
492 
493     @Test
testTsWithH264MpegAudio()494     public void testTsWithH264MpegAudio() throws IOException {
495         testAssetExtraction("media/ts/sample_h264_mpeg_audio.ts");
496     }
497 
498     @Test
testTsWithH264DetectAccessUnits()499     public void testTsWithH264DetectAccessUnits() throws IOException {
500         testAssetExtraction(
501                 "media/ts/sample_h264_no_access_unit_delimiters.ts",
502                 Collections.singletonMap(MediaParser.PARAMETER_TS_DETECT_ACCESS_UNITS, true));
503     }
504 
505     @Test
testTsWithH264DtsAudio()506     public void testTsWithH264DtsAudio() throws IOException {
507         testAssetExtraction(
508                 "media/ts/sample_h264_dts_audio.ts",
509                 Collections.singletonMap(
510                         MediaParser.PARAMETER_TS_ENABLE_HDMV_DTS_AUDIO_STREAMS, true));
511     }
512 
513     @Test
testTsWithLatm()514     public void testTsWithLatm() throws IOException {
515         testAssetExtraction("media/ts/sample_latm.ts");
516     }
517 
518     @Test
testTsWithSdt()519     public void testTsWithSdt() throws IOException {
520         testAssetExtraction("media/ts/sample_with_sdt.ts");
521     }
522 
523     @Test
testTsWithH265()524     public void testTsWithH265() throws IOException {
525         testAssetExtraction("media/ts/sample_h265.ts");
526     }
527 
528     // MKV.
529 
530     @Test
testMatroskaSubsampleEncryptedNoAltref()531     public void testMatroskaSubsampleEncryptedNoAltref() throws IOException {
532         testAssetExtraction("media/mkv/subsample_encrypted_noaltref.webm");
533     }
534 
535     @Test
testMatroskaFile()536     public void testMatroskaFile() throws IOException {
537         testAssetExtraction("media/mkv/sample.mkv");
538     }
539 
540     @Test
testMatroskaFullBlocks()541     public void testMatroskaFullBlocks() throws IOException {
542         testAssetExtraction("media/mkv/full_blocks.mkv");
543     }
544 
545     @Test
testMatroskaSubsampleEncryptedAltref()546     public void testMatroskaSubsampleEncryptedAltref() throws IOException {
547         testAssetExtraction("media/mkv/subsample_encrypted_altref.webm");
548     }
549 
550     @Test
testMatroskaOutOfBandCrypto()551     public void testMatroskaOutOfBandCrypto() throws IOException {
552         MockMediaParserOutputConsumer outputConsumer = new MockMediaParserOutputConsumer();
553         MockMediaParserInputReader inputReader =
554                 getInputReader("media/mkv/subsample_encrypted_noaltref.webm");
555         MediaParser mediaParser = MediaParser.create(outputConsumer);
556         // Initialization vectors are 16 bytes in size, as per CryptoInfo documentation.
557         MediaCodec.CryptoInfo expectedCryptoInfo = new MediaCodec.CryptoInfo();
558         expectedCryptoInfo.set(
559                 /* newNumSubsamples=*/ 1,
560                 /* newNumBytesOfClearData= */ new int[] {2},
561                 /* newNumBytesOfEncryptedData= */ new int[] {5},
562                 /* newKey= */ Arrays.copyOf(
563                         Util.getBytesFromHexString("01020304050607080910111213141516"), 16),
564                 /* newIv= */ Arrays.copyOf(Util.getBytesFromHexString("0101010101010101"), 16),
565                 MediaCodec.CRYPTO_MODE_AES_CTR);
566         advanceUntilSample(outputConsumer, inputReader, mediaParser, /* sampleNumber= */ 1);
567         assertEqual(outputConsumer.getLastOutputCryptoInfo(), expectedCryptoInfo);
568         mediaParser.release();
569     }
570 
571     // MP4.
572 
573     @Test
testMp4Ac4Fragmented()574     public void testMp4Ac4Fragmented() throws IOException {
575         testAssetExtraction("media/mp4/sample_ac4_fragmented.mp4");
576     }
577 
578     @Test
testMp4AndroidSlowMotion()579     public void testMp4AndroidSlowMotion() throws IOException {
580         testAssetExtraction("media/mp4/sample_android_slow_motion.mp4");
581     }
582 
583     @Test
testMp4FragmentedSei()584     public void testMp4FragmentedSei() throws IOException {
585         testAssetExtraction("media/mp4/sample_fragmented_sei.mp4");
586     }
587 
588     @Test
testMp4WithAc4()589     public void testMp4WithAc4() throws IOException {
590         testAssetExtraction("media/mp4/sample_ac4.mp4");
591     }
592 
593     @Test
testMp4FragmentedSeekable()594     public void testMp4FragmentedSeekable() throws IOException {
595         testAssetExtraction("media/mp4/sample_fragmented_seekable.mp4");
596     }
597 
598     @Test
testMp4WithProtectedAc4()599     public void testMp4WithProtectedAc4() throws IOException {
600         testAssetExtraction("media/mp4/sample_ac4_protected.mp4");
601     }
602 
603     @Test
testMp4()604     public void testMp4() throws IOException {
605         testAssetExtraction("media/mp4/sample.mp4");
606     }
607 
608     @Test
testMp4MdatTooLong()609     public void testMp4MdatTooLong() throws IOException {
610         testAssetExtraction("media/mp4/sample_mdat_too_long.mp4");
611     }
612 
613     @Test
testMp4Fragmented()614     public void testMp4Fragmented() throws IOException {
615         testAssetExtraction("media/mp4/sample_fragmented.mp4");
616     }
617 
618     @Test
testMp4FragmentedOutOfBandCrypto()619     public void testMp4FragmentedOutOfBandCrypto() throws IOException {
620         MockMediaParserOutputConsumer outputConsumer = new MockMediaParserOutputConsumer();
621         MockMediaParserInputReader inputReader =
622                 getInputReader("media/mp4/sample_ac4_protected.mp4");
623         MediaParser mediaParser = MediaParser.create(outputConsumer);
624         // Initialization vectors are 16 bytes in size, as per CryptoInfo documentation.
625         MediaCodec.CryptoInfo expectedCryptoInfo = new MediaCodec.CryptoInfo();
626         expectedCryptoInfo.set(
627                 /* newNumSubsamples=*/ 1,
628                 /* newNumBytesOfClearData= */ new int[] {7},
629                 /* newNumBytesOfEncryptedData= */ new int[] {360},
630                 /* newKey= */ Arrays.copyOf(
631                         Util.getBytesFromHexString("91341951696b5e1ba232439ecec1f12a"), 16),
632                 /* newIv= */ Arrays.copyOf(Util.getBytesFromHexString("aab4ed0108dd5267"), 16),
633                 MediaCodec.CRYPTO_MODE_AES_CTR);
634         advanceUntilSample(outputConsumer, inputReader, mediaParser, /* sampleNumber= */ 1);
635         assertEqual(outputConsumer.getLastOutputCryptoInfo(), expectedCryptoInfo);
636 
637         expectedCryptoInfo.iv = Arrays.copyOf(Util.getBytesFromHexString("aab4ed0108dd5272"), 16);
638         expectedCryptoInfo.numBytesOfEncryptedData = new int[] {488};
639         advanceUntilSample(outputConsumer, inputReader, mediaParser, /* sampleNumber= */ 12);
640         assertEqual(outputConsumer.getLastOutputCryptoInfo(), expectedCryptoInfo);
641 
642         mediaParser.release();
643     }
644 
645     // Internal methods.
646 
testCreationByName(String name)647     private static void testCreationByName(String name) {
648         MediaParser.createByName(name, new MockMediaParserOutputConsumer()).release();
649     }
650 
assertSupportFor(String parameterName)651     private static void assertSupportFor(String parameterName) {
652         MediaParser mediaParser = MediaParser.create(new MockMediaParserOutputConsumer());
653         assertThat(mediaParser.supportsParameter(parameterName)).isTrue();
654         mediaParser.release();
655     }
656 
testValidAndInvalidValueForBooleanParameter(String parameterName)657     private static void testValidAndInvalidValueForBooleanParameter(String parameterName) {
658         testParameterSetting(parameterName, /* value= */ "string", /* valueIsIllegal= */ true);
659         testParameterSetting(parameterName, /* value= */ true, /* valueIsIllegal= */ false);
660     }
661 
testParameterSetting( String parameterName, Object value, boolean valueIsIllegal)662     private static void testParameterSetting(
663             String parameterName, Object value, boolean valueIsIllegal) {
664         MediaParser mediaParser = MediaParser.create(new MockMediaParserOutputConsumer());
665         boolean illegalArgument = false;
666         try {
667             mediaParser.setParameter(parameterName, value);
668         } catch (IllegalArgumentException e) {
669             illegalArgument = true;
670         }
671         if (valueIsIllegal != illegalArgument) {
672             fail();
673         }
674     }
675 
testSniffAsset(String assetPath, String expectedParserName)676     private static void testSniffAsset(String assetPath, String expectedParserName)
677             throws IOException {
678         testAssetExtraction(assetPath, Collections.emptyMap(), expectedParserName);
679     }
680 
testAssetExtraction(String assetPath)681     private static void testAssetExtraction(String assetPath) throws IOException {
682         testAssetExtraction(assetPath, Collections.emptyMap());
683     }
684 
testAssetExtraction(String assetPath, Map<String, Object> parameters)685     private static void testAssetExtraction(String assetPath, Map<String, Object> parameters)
686             throws IOException {
687         testAssetExtraction(assetPath, parameters, /* expectedParserName= */ null);
688     }
689 
testAssetExtraction( String assetPath, Map<String, Object> parameters, String expectedParserName)690     private static void testAssetExtraction(
691             String assetPath, Map<String, Object> parameters, String expectedParserName)
692             throws IOException {
693         MockMediaParserInputReader inputReader = getInputReader(assetPath);
694         boolean usingInBandCryptoInfo =
695                 (boolean) parameters.getOrDefault(PARAMETER_IN_BAND_CRYPTO_INFO, false);
696         MockMediaParserOutputConsumer outputConsumer =
697                 new MockMediaParserOutputConsumer(usingInBandCryptoInfo);
698         MediaParser mediaParser = MediaParser.create(outputConsumer);
699         for (Map.Entry<String, Object> entry : parameters.entrySet()) {
700             mediaParser.setParameter(entry.getKey(), entry.getValue());
701         }
702 
703         try {
704             mediaParser.advance(inputReader);
705             if (expectedParserName != null) {
706                 assertThat(expectedParserName).isEqualTo(mediaParser.getParserName());
707                 // We are only checking that the extractor is the right one.
708                 return;
709             }
710 
711             while (mediaParser.advance(inputReader)) {
712                 // Do nothing.
713             }
714 
715             // If the SeekMap is seekable, test seeking in the stream.
716             MediaParser.SeekMap seekMap = outputConsumer.getSeekMap();
717             assertThat(seekMap).isNotNull();
718             if (seekMap.isSeekable()) {
719                 long durationUs = seekMap.getDurationMicros();
720                 for (int j = 0; j < 4; j++) {
721                     outputConsumer.clearTrackOutputs();
722                     long timeUs =
723                             durationUs == MediaParser.SeekMap.UNKNOWN_DURATION
724                                     ? 0
725                                     : (durationUs * j) / 3;
726                     MediaParser.SeekPoint seekPoint = seekMap.getSeekPoints(timeUs).first;
727                     inputReader.reset();
728                     inputReader.setPosition((int) seekPoint.position);
729                     mediaParser.seek(seekPoint);
730                     while (mediaParser.advance(inputReader)) {
731                         // Do nothing.
732                     }
733                     if (durationUs == MediaParser.SeekMap.UNKNOWN_DURATION) {
734                         break;
735                     }
736                 }
737             }
738         } finally {
739             mediaParser.release();
740         }
741     }
742 
getInputReader(String assetPath)743     private static MockMediaParserInputReader getInputReader(String assetPath) throws IOException {
744         byte[] assetBytes =
745                 TestUtil.getByteArray(
746                         InstrumentationRegistry.getInstrumentation().getContext(), assetPath);
747         return new MockMediaParserInputReader(
748                 new FakeExtractorInput.Builder().setData(assetBytes).build());
749     }
750 
advanceUntilSample( MockMediaParserOutputConsumer outputConsumer, MockMediaParserInputReader inputReader, MediaParser mediaParser, int sampleNumber)751     private static void advanceUntilSample(
752             MockMediaParserOutputConsumer outputConsumer,
753             MockMediaParserInputReader inputReader,
754             MediaParser mediaParser,
755             int sampleNumber)
756             throws IOException {
757         while (outputConsumer.getCompletedSampleCount() != sampleNumber) {
758             assertThat(mediaParser.advance(inputReader)).isTrue();
759         }
760     }
761 
assertEqual( MediaCodec.CryptoInfo actualCryptoInfo, MediaCodec.CryptoInfo expectedCryptoInfo)762     private static void assertEqual(
763             MediaCodec.CryptoInfo actualCryptoInfo, MediaCodec.CryptoInfo expectedCryptoInfo) {
764         assertThat(actualCryptoInfo.mode).isEqualTo(expectedCryptoInfo.mode);
765         assertThat(actualCryptoInfo.key).isEqualTo(expectedCryptoInfo.key);
766         assertThat(actualCryptoInfo.iv).isEqualTo(expectedCryptoInfo.iv);
767         assertThat(actualCryptoInfo.numSubSamples).isEqualTo(expectedCryptoInfo.numSubSamples);
768         for (int i = 0; i < actualCryptoInfo.numSubSamples; i++) {
769             assertThat(actualCryptoInfo.numBytesOfClearData[i])
770                     .isEqualTo(expectedCryptoInfo.numBytesOfClearData[i]);
771             assertThat(actualCryptoInfo.numBytesOfEncryptedData[i])
772                     .isEqualTo(expectedCryptoInfo.numBytesOfEncryptedData[i]);
773         }
774     }
775 
776     // Internal classes.
777 
assertParsers(String... names)778     private static FluentMediaParserSubject assertParsers(String... names) {
779         return new FluentMediaParserSubject(names);
780     }
781 
782     private static final class FluentMediaParserSubject {
783 
784         private final String[] mediaParserNames;
785 
FluentMediaParserSubject(String[] mediaParserNames)786         private FluentMediaParserSubject(String[] mediaParserNames) {
787             this.mediaParserNames = mediaParserNames;
788         }
789 
supportMimeTypes(String... mimeTypes)790         public void supportMimeTypes(String... mimeTypes) {
791             for (String mimeType : mimeTypes) {
792                 MediaFormat format = new MediaFormat();
793                 format.setString(MediaFormat.KEY_MIME, mimeType);
794                 assertThat(MediaParser.getParserNames(format))
795                         .containsExactlyElementsIn(mediaParserNames);
796             }
797         }
798     }
799 }
800