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