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 package android.media; 17 18 import android.annotation.CheckResult; 19 import android.annotation.IntDef; 20 import android.annotation.NonNull; 21 import android.annotation.Nullable; 22 import android.annotation.StringDef; 23 import android.media.MediaCodec.CryptoInfo; 24 import android.os.Build; 25 import android.text.TextUtils; 26 import android.util.Log; 27 import android.util.Pair; 28 import android.util.SparseArray; 29 30 import com.google.android.exoplayer2.C; 31 import com.google.android.exoplayer2.Format; 32 import com.google.android.exoplayer2.ParserException; 33 import com.google.android.exoplayer2.drm.DrmInitData.SchemeData; 34 import com.google.android.exoplayer2.extractor.ChunkIndex; 35 import com.google.android.exoplayer2.extractor.DefaultExtractorInput; 36 import com.google.android.exoplayer2.extractor.Extractor; 37 import com.google.android.exoplayer2.extractor.ExtractorInput; 38 import com.google.android.exoplayer2.extractor.ExtractorOutput; 39 import com.google.android.exoplayer2.extractor.PositionHolder; 40 import com.google.android.exoplayer2.extractor.SeekMap.SeekPoints; 41 import com.google.android.exoplayer2.extractor.TrackOutput; 42 import com.google.android.exoplayer2.extractor.amr.AmrExtractor; 43 import com.google.android.exoplayer2.extractor.flac.FlacExtractor; 44 import com.google.android.exoplayer2.extractor.flv.FlvExtractor; 45 import com.google.android.exoplayer2.extractor.mkv.MatroskaExtractor; 46 import com.google.android.exoplayer2.extractor.mp3.Mp3Extractor; 47 import com.google.android.exoplayer2.extractor.mp4.FragmentedMp4Extractor; 48 import com.google.android.exoplayer2.extractor.mp4.Mp4Extractor; 49 import com.google.android.exoplayer2.extractor.ogg.OggExtractor; 50 import com.google.android.exoplayer2.extractor.ts.Ac3Extractor; 51 import com.google.android.exoplayer2.extractor.ts.Ac4Extractor; 52 import com.google.android.exoplayer2.extractor.ts.AdtsExtractor; 53 import com.google.android.exoplayer2.extractor.ts.DefaultTsPayloadReaderFactory; 54 import com.google.android.exoplayer2.extractor.ts.PsExtractor; 55 import com.google.android.exoplayer2.extractor.ts.TsExtractor; 56 import com.google.android.exoplayer2.extractor.wav.WavExtractor; 57 import com.google.android.exoplayer2.upstream.DataReader; 58 import com.google.android.exoplayer2.util.ParsableByteArray; 59 import com.google.android.exoplayer2.util.TimestampAdjuster; 60 import com.google.android.exoplayer2.util.Util; 61 import com.google.android.exoplayer2.video.ColorInfo; 62 63 import java.io.EOFException; 64 import java.io.IOException; 65 import java.lang.annotation.Retention; 66 import java.lang.annotation.RetentionPolicy; 67 import java.lang.reflect.Constructor; 68 import java.lang.reflect.InvocationTargetException; 69 import java.nio.ByteBuffer; 70 import java.util.ArrayList; 71 import java.util.Arrays; 72 import java.util.Collections; 73 import java.util.HashMap; 74 import java.util.LinkedHashMap; 75 import java.util.List; 76 import java.util.Map; 77 import java.util.UUID; 78 79 /** 80 * Parses media container formats and extracts contained media samples and metadata. 81 * 82 * <p>This class provides access to a battery of low-level media container parsers. Each instance of 83 * this class is associated to a specific media parser implementation which is suitable for 84 * extraction from a specific media container format. The media parser implementation assignment 85 * depends on the factory method (see {@link #create} and {@link #createByName}) used to create the 86 * instance. 87 * 88 * <p>Users must implement the following to use this class. 89 * 90 * <ul> 91 * <li>{@link InputReader}: Provides the media container's bytes to parse. 92 * <li>{@link OutputConsumer}: Provides a sink for all extracted data and metadata. 93 * </ul> 94 * 95 * <p>The following code snippet includes a usage example: 96 * 97 * <pre> 98 * MyOutputConsumer myOutputConsumer = new MyOutputConsumer(); 99 * MyInputReader myInputReader = new MyInputReader("www.example.com"); 100 * MediaParser mediaParser = MediaParser.create(myOutputConsumer); 101 * 102 * while (mediaParser.advance(myInputReader)) {} 103 * 104 * mediaParser.release(); 105 * mediaParser = null; 106 * </pre> 107 * 108 * <p>The following code snippet provides a rudimentary {@link OutputConsumer} sample implementation 109 * which extracts and publishes all video samples: 110 * 111 * <pre> 112 * class VideoOutputConsumer implements MediaParser.OutputConsumer { 113 * 114 * private byte[] sampleDataBuffer = new byte[4096]; 115 * private byte[] discardedDataBuffer = new byte[4096]; 116 * private int videoTrackIndex = -1; 117 * private int bytesWrittenCount = 0; 118 * 119 * @Override 120 * public void onSeekMapFound(int i, @NonNull MediaFormat mediaFormat) { 121 * // Do nothing. 122 * } 123 * 124 * @Override 125 * public void onTrackDataFound(int i, @NonNull TrackData trackData) { 126 * MediaFormat mediaFormat = trackData.mediaFormat; 127 * if (videoTrackIndex == -1 && 128 * mediaFormat 129 * .getString(MediaFormat.KEY_MIME, /* defaultValue= */ "") 130 * .startsWith("video/")) { 131 * videoTrackIndex = i; 132 * } 133 * } 134 * 135 * @Override 136 * public void onSampleDataFound(int trackIndex, @NonNull InputReader inputReader) 137 * throws IOException { 138 * int numberOfBytesToRead = (int) inputReader.getLength(); 139 * if (videoTrackIndex != trackIndex) { 140 * // Discard contents. 141 * inputReader.read( 142 * discardedDataBuffer, 143 * /* offset= */ 0, 144 * Math.min(discardDataBuffer.length, numberOfBytesToRead)); 145 * } else { 146 * ensureSpaceInBuffer(numberOfBytesToRead); 147 * int bytesRead = inputReader.read( 148 * sampleDataBuffer, bytesWrittenCount, numberOfBytesToRead); 149 * bytesWrittenCount += bytesRead; 150 * } 151 * } 152 * 153 * @Override 154 * public void onSampleCompleted( 155 * int trackIndex, 156 * long timeMicros, 157 * int flags, 158 * int size, 159 * int offset, 160 * @Nullable CryptoInfo cryptoData) { 161 * if (videoTrackIndex != trackIndex) { 162 * return; // It's not the video track. Ignore. 163 * } 164 * byte[] sampleData = new byte[size]; 165 * int sampleStartOffset = bytesWrittenCount - size - offset; 166 * System.arraycopy( 167 * sampleDataBuffer, 168 * sampleStartOffset, 169 * sampleData, 170 * /* destPos= */ 0, 171 * size); 172 * // Place trailing bytes at the start of the buffer. 173 * System.arraycopy( 174 * sampleDataBuffer, 175 * bytesWrittenCount - offset, 176 * sampleDataBuffer, 177 * /* destPos= */ 0, 178 * /* size= */ offset); 179 * bytesWrittenCount = bytesWrittenCount - offset; 180 * publishSample(sampleData, timeMicros, flags); 181 * } 182 * 183 * private void ensureSpaceInBuffer(int numberOfBytesToRead) { 184 * int requiredLength = bytesWrittenCount + numberOfBytesToRead; 185 * if (requiredLength > sampleDataBuffer.length) { 186 * sampleDataBuffer = Arrays.copyOf(sampleDataBuffer, requiredLength); 187 * } 188 * } 189 * 190 * } 191 * 192 * </pre> 193 */ 194 public final class MediaParser { 195 196 /** 197 * Maps seek positions to {@link SeekPoint SeekPoints} in the stream. 198 * 199 * <p>A {@link SeekPoint} is a position in the stream from which a player may successfully start 200 * playing media samples. 201 */ 202 public static final class SeekMap { 203 204 /** Returned by {@link #getDurationMicros()} when the duration is unknown. */ 205 public static final int UNKNOWN_DURATION = Integer.MIN_VALUE; 206 207 /** 208 * For each {@link #getSeekPoints} call, returns a single {@link SeekPoint} whose {@link 209 * SeekPoint#timeMicros} matches the requested timestamp, and whose {@link 210 * SeekPoint#position} is 0. 211 * 212 * @hide 213 */ 214 public static final SeekMap DUMMY = new SeekMap(new DummyExoPlayerSeekMap()); 215 216 private final com.google.android.exoplayer2.extractor.SeekMap mExoPlayerSeekMap; 217 SeekMap(com.google.android.exoplayer2.extractor.SeekMap exoplayerSeekMap)218 private SeekMap(com.google.android.exoplayer2.extractor.SeekMap exoplayerSeekMap) { 219 mExoPlayerSeekMap = exoplayerSeekMap; 220 } 221 222 /** Returns whether seeking is supported. */ isSeekable()223 public boolean isSeekable() { 224 return mExoPlayerSeekMap.isSeekable(); 225 } 226 227 /** 228 * Returns the duration of the stream in microseconds or {@link #UNKNOWN_DURATION} if the 229 * duration is unknown. 230 */ getDurationMicros()231 public long getDurationMicros() { 232 long durationUs = mExoPlayerSeekMap.getDurationUs(); 233 return durationUs != C.TIME_UNSET ? durationUs : UNKNOWN_DURATION; 234 } 235 236 /** 237 * Obtains {@link SeekPoint SeekPoints} for the specified seek time in microseconds. 238 * 239 * <p>{@code getSeekPoints(timeMicros).first} contains the latest seek point for samples 240 * with timestamp equal to or smaller than {@code timeMicros}. 241 * 242 * <p>{@code getSeekPoints(timeMicros).second} contains the earliest seek point for samples 243 * with timestamp equal to or greater than {@code timeMicros}. If a seek point exists for 244 * {@code timeMicros}, the returned pair will contain the same {@link SeekPoint} twice. 245 * 246 * @param timeMicros A seek time in microseconds. 247 * @return The corresponding {@link SeekPoint SeekPoints}. 248 */ 249 @NonNull getSeekPoints(long timeMicros)250 public Pair<SeekPoint, SeekPoint> getSeekPoints(long timeMicros) { 251 SeekPoints seekPoints = mExoPlayerSeekMap.getSeekPoints(timeMicros); 252 return new Pair<>(toSeekPoint(seekPoints.first), toSeekPoint(seekPoints.second)); 253 } 254 } 255 256 /** Holds information associated with a track. */ 257 public static final class TrackData { 258 259 /** Holds {@link MediaFormat} information for the track. */ 260 @NonNull public final MediaFormat mediaFormat; 261 262 /** 263 * Holds {@link DrmInitData} necessary to acquire keys associated with the track, or null if 264 * the track has no encryption data. 265 */ 266 @Nullable public final DrmInitData drmInitData; 267 TrackData(MediaFormat mediaFormat, DrmInitData drmInitData)268 private TrackData(MediaFormat mediaFormat, DrmInitData drmInitData) { 269 this.mediaFormat = mediaFormat; 270 this.drmInitData = drmInitData; 271 } 272 } 273 274 /** Defines a seek point in a media stream. */ 275 public static final class SeekPoint { 276 277 /** A {@link SeekPoint} whose time and byte offset are both set to 0. */ 278 @NonNull public static final SeekPoint START = new SeekPoint(0, 0); 279 280 /** The time of the seek point, in microseconds. */ 281 public final long timeMicros; 282 283 /** The byte offset of the seek point. */ 284 public final long position; 285 286 /** 287 * @param timeMicros The time of the seek point, in microseconds. 288 * @param position The byte offset of the seek point. 289 */ SeekPoint(long timeMicros, long position)290 private SeekPoint(long timeMicros, long position) { 291 this.timeMicros = timeMicros; 292 this.position = position; 293 } 294 295 @Override 296 @NonNull toString()297 public String toString() { 298 return "[timeMicros=" + timeMicros + ", position=" + position + "]"; 299 } 300 301 @Override equals(@ullable Object obj)302 public boolean equals(@Nullable Object obj) { 303 if (this == obj) { 304 return true; 305 } 306 if (obj == null || getClass() != obj.getClass()) { 307 return false; 308 } 309 SeekPoint other = (SeekPoint) obj; 310 return timeMicros == other.timeMicros && position == other.position; 311 } 312 313 @Override hashCode()314 public int hashCode() { 315 int result = (int) timeMicros; 316 result = 31 * result + (int) position; 317 return result; 318 } 319 } 320 321 /** Provides input data to {@link MediaParser}. */ 322 public interface InputReader { 323 324 /** 325 * Reads up to {@code readLength} bytes of data and stores them into {@code buffer}, 326 * starting at index {@code offset}. 327 * 328 * <p>This method blocks until at least one byte is read, the end of input is detected, or 329 * an exception is thrown. The read position advances to the first unread byte. 330 * 331 * @param buffer The buffer into which the read data should be stored. 332 * @param offset The start offset into {@code buffer} at which data should be written. 333 * @param readLength The maximum number of bytes to read. 334 * @return The non-zero number of bytes read, or -1 if no data is available because the end 335 * of the input has been reached. 336 * @throws java.io.IOException If an error occurs reading from the source. 337 */ read(@onNull byte[] buffer, int offset, int readLength)338 int read(@NonNull byte[] buffer, int offset, int readLength) throws IOException; 339 340 /** Returns the current read position (byte offset) in the stream. */ getPosition()341 long getPosition(); 342 343 /** Returns the length of the input in bytes, or -1 if the length is unknown. */ getLength()344 long getLength(); 345 } 346 347 /** {@link InputReader} that allows setting the read position. */ 348 public interface SeekableInputReader extends InputReader { 349 350 /** 351 * Sets the read position at the given {@code position}. 352 * 353 * <p>{@link #advance} will immediately return after calling this method. 354 * 355 * @param position The position to seek to, in bytes. 356 */ seekToPosition(long position)357 void seekToPosition(long position); 358 } 359 360 /** Receives extracted media sample data and metadata from {@link MediaParser}. */ 361 public interface OutputConsumer { 362 363 /** 364 * Called when a {@link SeekMap} has been extracted from the stream. 365 * 366 * <p>This method is called at least once before any samples are {@link #onSampleCompleted 367 * complete}. May be called multiple times after that in order to add {@link SeekPoint 368 * SeekPoints}. 369 * 370 * @param seekMap The extracted {@link SeekMap}. 371 */ onSeekMapFound(@onNull SeekMap seekMap)372 void onSeekMapFound(@NonNull SeekMap seekMap); 373 374 /** 375 * Called when the number of tracks is found. 376 * 377 * @param numberOfTracks The number of tracks in the stream. 378 */ onTrackCountFound(int numberOfTracks)379 void onTrackCountFound(int numberOfTracks); 380 381 /** 382 * Called when new {@link TrackData} is found in the stream. 383 * 384 * @param trackIndex The index of the track for which the {@link TrackData} was extracted. 385 * @param trackData The extracted {@link TrackData}. 386 */ onTrackDataFound(int trackIndex, @NonNull TrackData trackData)387 void onTrackDataFound(int trackIndex, @NonNull TrackData trackData); 388 389 /** 390 * Called when sample data is found in the stream. 391 * 392 * <p>If the invocation of this method returns before the entire {@code inputReader} {@link 393 * InputReader#getLength() length} is consumed, the method will be called again for the 394 * implementer to read the remaining data. Implementers should surface any thrown {@link 395 * IOException} caused by reading from {@code input}. 396 * 397 * @param trackIndex The index of the track to which the sample data corresponds. 398 * @param inputReader The {@link InputReader} from which to read the data. 399 * @throws IOException If an exception occurs while reading from {@code inputReader}. 400 */ onSampleDataFound(int trackIndex, @NonNull InputReader inputReader)401 void onSampleDataFound(int trackIndex, @NonNull InputReader inputReader) throws IOException; 402 403 /** 404 * Called once all the data of a sample has been passed to {@link #onSampleDataFound}. 405 * 406 * <p>Includes sample metadata, like presentation timestamp and flags. 407 * 408 * @param trackIndex The index of the track to which the sample corresponds. 409 * @param timeMicros The media timestamp associated with the sample, in microseconds. 410 * @param flags Flags associated with the sample. See the {@code SAMPLE_FLAG_*} constants. 411 * @param size The size of the sample data, in bytes. 412 * @param offset The number of bytes that have been consumed by {@code 413 * onSampleDataFound(int, MediaParser.InputReader)} for the specified track, since the 414 * last byte belonging to the sample whose metadata is being passed. 415 * @param cryptoInfo Encryption data required to decrypt the sample. May be null for 416 * unencrypted samples. Implementors should treat any output {@link CryptoInfo} 417 * instances as immutable. MediaParser will not modify any output {@code cryptoInfos} 418 * and implementors should not modify them either. 419 */ onSampleCompleted( int trackIndex, long timeMicros, @SampleFlags int flags, int size, int offset, @Nullable CryptoInfo cryptoInfo)420 void onSampleCompleted( 421 int trackIndex, 422 long timeMicros, 423 @SampleFlags int flags, 424 int size, 425 int offset, 426 @Nullable CryptoInfo cryptoInfo); 427 } 428 429 /** 430 * Thrown if all parser implementations provided to {@link #create} failed to sniff the input 431 * content. 432 */ 433 public static final class UnrecognizedInputFormatException extends IOException { 434 435 /** 436 * Creates a new instance which signals that the parsers with the given names failed to 437 * parse the input. 438 */ 439 @NonNull 440 @CheckResult createForExtractors( @onNull String... extractorNames)441 private static UnrecognizedInputFormatException createForExtractors( 442 @NonNull String... extractorNames) { 443 StringBuilder builder = new StringBuilder(); 444 builder.append("None of the available parsers ( "); 445 builder.append(extractorNames[0]); 446 for (int i = 1; i < extractorNames.length; i++) { 447 builder.append(", "); 448 builder.append(extractorNames[i]); 449 } 450 builder.append(") could read the stream."); 451 return new UnrecognizedInputFormatException(builder.toString()); 452 } 453 UnrecognizedInputFormatException(String extractorNames)454 private UnrecognizedInputFormatException(String extractorNames) { 455 super(extractorNames); 456 } 457 } 458 459 /** Thrown when an error occurs while parsing a media stream. */ 460 public static final class ParsingException extends IOException { 461 ParsingException(ParserException cause)462 private ParsingException(ParserException cause) { 463 super(cause); 464 } 465 } 466 467 // Sample flags. 468 469 /** @hide */ 470 @Retention(RetentionPolicy.SOURCE) 471 @IntDef( 472 flag = true, 473 value = { 474 SAMPLE_FLAG_KEY_FRAME, 475 SAMPLE_FLAG_HAS_SUPPLEMENTAL_DATA, 476 SAMPLE_FLAG_LAST_SAMPLE, 477 SAMPLE_FLAG_ENCRYPTED, 478 SAMPLE_FLAG_DECODE_ONLY 479 }) 480 public @interface SampleFlags {} 481 /** Indicates that the sample holds a synchronization sample. */ 482 public static final int SAMPLE_FLAG_KEY_FRAME = MediaCodec.BUFFER_FLAG_KEY_FRAME; 483 /** 484 * Indicates that the sample has supplemental data. 485 * 486 * <p>Samples will not have this flag set unless the {@code 487 * "android.media.mediaparser.includeSupplementalData"} parameter is set to {@code true} via 488 * {@link #setParameter}. 489 * 490 * <p>Samples with supplemental data have the following sample data format: 491 * 492 * <ul> 493 * <li>If the {@code "android.media.mediaparser.inBandCryptoInfo"} parameter is set, all 494 * encryption information. 495 * <li>(4 bytes) {@code sample_data_size}: The size of the actual sample data, not including 496 * supplemental data or encryption information. 497 * <li>({@code sample_data_size} bytes): The media sample data. 498 * <li>(remaining bytes) The supplemental data. 499 * </ul> 500 */ 501 public static final int SAMPLE_FLAG_HAS_SUPPLEMENTAL_DATA = 1 << 28; 502 /** Indicates that the sample is known to contain the last media sample of the stream. */ 503 public static final int SAMPLE_FLAG_LAST_SAMPLE = 1 << 29; 504 /** Indicates that the sample is (at least partially) encrypted. */ 505 public static final int SAMPLE_FLAG_ENCRYPTED = 1 << 30; 506 /** Indicates that the sample should be decoded but not rendered. */ 507 public static final int SAMPLE_FLAG_DECODE_ONLY = 1 << 31; 508 509 // Parser implementation names. 510 511 /** @hide */ 512 @Retention(RetentionPolicy.SOURCE) 513 @StringDef( 514 prefix = {"PARSER_NAME_"}, 515 value = { 516 PARSER_NAME_UNKNOWN, 517 PARSER_NAME_MATROSKA, 518 PARSER_NAME_FMP4, 519 PARSER_NAME_MP4, 520 PARSER_NAME_MP3, 521 PARSER_NAME_ADTS, 522 PARSER_NAME_AC3, 523 PARSER_NAME_TS, 524 PARSER_NAME_FLV, 525 PARSER_NAME_OGG, 526 PARSER_NAME_PS, 527 PARSER_NAME_WAV, 528 PARSER_NAME_AMR, 529 PARSER_NAME_AC4, 530 PARSER_NAME_FLAC 531 }) 532 public @interface ParserName {} 533 534 /** Parser name returned by {@link #getParserName()} when no parser has been selected yet. */ 535 public static final String PARSER_NAME_UNKNOWN = "android.media.mediaparser.UNKNOWN"; 536 /** 537 * Parser for the Matroska container format, as defined in the <a 538 * href="https://matroska.org/technical/specs/">spec</a>. 539 */ 540 public static final String PARSER_NAME_MATROSKA = "android.media.mediaparser.MatroskaParser"; 541 /** 542 * Parser for fragmented files using the MP4 container format, as defined in ISO/IEC 14496-12. 543 */ 544 public static final String PARSER_NAME_FMP4 = "android.media.mediaparser.FragmentedMp4Parser"; 545 /** 546 * Parser for non-fragmented files using the MP4 container format, as defined in ISO/IEC 547 * 14496-12. 548 */ 549 public static final String PARSER_NAME_MP4 = "android.media.mediaparser.Mp4Parser"; 550 /** Parser for the MP3 container format, as defined in ISO/IEC 11172-3. */ 551 public static final String PARSER_NAME_MP3 = "android.media.mediaparser.Mp3Parser"; 552 /** Parser for the ADTS container format, as defined in ISO/IEC 13818-7. */ 553 public static final String PARSER_NAME_ADTS = "android.media.mediaparser.AdtsParser"; 554 /** 555 * Parser for the AC-3 container format, as defined in Digital Audio Compression Standard 556 * (AC-3). 557 */ 558 public static final String PARSER_NAME_AC3 = "android.media.mediaparser.Ac3Parser"; 559 /** Parser for the TS container format, as defined in ISO/IEC 13818-1. */ 560 public static final String PARSER_NAME_TS = "android.media.mediaparser.TsParser"; 561 /** 562 * Parser for the FLV container format, as defined in Adobe Flash Video File Format 563 * Specification. 564 */ 565 public static final String PARSER_NAME_FLV = "android.media.mediaparser.FlvParser"; 566 /** Parser for the OGG container format, as defined in RFC 3533. */ 567 public static final String PARSER_NAME_OGG = "android.media.mediaparser.OggParser"; 568 /** Parser for the PS container format, as defined in ISO/IEC 11172-1. */ 569 public static final String PARSER_NAME_PS = "android.media.mediaparser.PsParser"; 570 /** 571 * Parser for the WAV container format, as defined in Multimedia Programming Interface and Data 572 * Specifications. 573 */ 574 public static final String PARSER_NAME_WAV = "android.media.mediaparser.WavParser"; 575 /** Parser for the AMR container format, as defined in RFC 4867. */ 576 public static final String PARSER_NAME_AMR = "android.media.mediaparser.AmrParser"; 577 /** 578 * Parser for the AC-4 container format, as defined by Dolby AC-4: Audio delivery for 579 * Next-Generation Entertainment Services. 580 */ 581 public static final String PARSER_NAME_AC4 = "android.media.mediaparser.Ac4Parser"; 582 /** 583 * Parser for the FLAC container format, as defined in the <a 584 * href="https://xiph.org/flac/">spec</a>. 585 */ 586 public static final String PARSER_NAME_FLAC = "android.media.mediaparser.FlacParser"; 587 588 // MediaParser parameters. 589 590 /** @hide */ 591 @Retention(RetentionPolicy.SOURCE) 592 @StringDef( 593 prefix = {"PARAMETER_"}, 594 value = { 595 PARAMETER_ADTS_ENABLE_CBR_SEEKING, 596 PARAMETER_AMR_ENABLE_CBR_SEEKING, 597 PARAMETER_FLAC_DISABLE_ID3, 598 PARAMETER_MP4_IGNORE_EDIT_LISTS, 599 PARAMETER_MP4_IGNORE_TFDT_BOX, 600 PARAMETER_MP4_TREAT_VIDEO_FRAMES_AS_KEYFRAMES, 601 PARAMETER_MATROSKA_DISABLE_CUES_SEEKING, 602 PARAMETER_MP3_DISABLE_ID3, 603 PARAMETER_MP3_ENABLE_CBR_SEEKING, 604 PARAMETER_MP3_ENABLE_INDEX_SEEKING, 605 PARAMETER_TS_MODE, 606 PARAMETER_TS_ALLOW_NON_IDR_AVC_KEYFRAMES, 607 PARAMETER_TS_IGNORE_AAC_STREAM, 608 PARAMETER_TS_IGNORE_AVC_STREAM, 609 PARAMETER_TS_IGNORE_SPLICE_INFO_STREAM, 610 PARAMETER_TS_DETECT_ACCESS_UNITS, 611 PARAMETER_TS_ENABLE_HDMV_DTS_AUDIO_STREAMS, 612 PARAMETER_IN_BAND_CRYPTO_INFO, 613 PARAMETER_INCLUDE_SUPPLEMENTAL_DATA 614 }) 615 public @interface ParameterName {} 616 617 /** 618 * Sets whether constant bitrate seeking should be enabled for ADTS parsing. {@code boolean} 619 * expected. Default value is {@code false}. 620 */ 621 public static final String PARAMETER_ADTS_ENABLE_CBR_SEEKING = 622 "android.media.mediaparser.adts.enableCbrSeeking"; 623 /** 624 * Sets whether constant bitrate seeking should be enabled for AMR. {@code boolean} expected. 625 * Default value is {@code false}. 626 */ 627 public static final String PARAMETER_AMR_ENABLE_CBR_SEEKING = 628 "android.media.mediaparser.amr.enableCbrSeeking"; 629 /** 630 * Sets whether the ID3 track should be disabled for FLAC. {@code boolean} expected. Default 631 * value is {@code false}. 632 */ 633 public static final String PARAMETER_FLAC_DISABLE_ID3 = 634 "android.media.mediaparser.flac.disableId3"; 635 /** 636 * Sets whether MP4 parsing should ignore edit lists. {@code boolean} expected. Default value is 637 * {@code false}. 638 */ 639 public static final String PARAMETER_MP4_IGNORE_EDIT_LISTS = 640 "android.media.mediaparser.mp4.ignoreEditLists"; 641 /** 642 * Sets whether MP4 parsing should ignore the tfdt box. {@code boolean} expected. Default value 643 * is {@code false}. 644 */ 645 public static final String PARAMETER_MP4_IGNORE_TFDT_BOX = 646 "android.media.mediaparser.mp4.ignoreTfdtBox"; 647 /** 648 * Sets whether MP4 parsing should treat all video frames as key frames. {@code boolean} 649 * expected. Default value is {@code false}. 650 */ 651 public static final String PARAMETER_MP4_TREAT_VIDEO_FRAMES_AS_KEYFRAMES = 652 "android.media.mediaparser.mp4.treatVideoFramesAsKeyframes"; 653 /** 654 * Sets whether Matroska parsing should avoid seeking to the cues element. {@code boolean} 655 * expected. Default value is {@code false}. 656 * 657 * <p>If this flag is enabled and the cues element occurs after the first cluster, then the 658 * media is treated as unseekable. 659 */ 660 public static final String PARAMETER_MATROSKA_DISABLE_CUES_SEEKING = 661 "android.media.mediaparser.matroska.disableCuesSeeking"; 662 /** 663 * Sets whether the ID3 track should be disabled for MP3. {@code boolean} expected. Default 664 * value is {@code false}. 665 */ 666 public static final String PARAMETER_MP3_DISABLE_ID3 = 667 "android.media.mediaparser.mp3.disableId3"; 668 /** 669 * Sets whether constant bitrate seeking should be enabled for MP3. {@code boolean} expected. 670 * Default value is {@code false}. 671 */ 672 public static final String PARAMETER_MP3_ENABLE_CBR_SEEKING = 673 "android.media.mediaparser.mp3.enableCbrSeeking"; 674 /** 675 * Sets whether MP3 parsing should generate a time-to-byte mapping. {@code boolean} expected. 676 * Default value is {@code false}. 677 * 678 * <p>Enabling this flag may require to scan a significant portion of the file to compute a seek 679 * point. Therefore, it should only be used if: 680 * 681 * <ul> 682 * <li>the file is small, or 683 * <li>the bitrate is variable (or the type of bitrate is unknown) and the seeking metadata 684 * provided in the file is not precise enough (or is not present). 685 * </ul> 686 */ 687 public static final String PARAMETER_MP3_ENABLE_INDEX_SEEKING = 688 "android.media.mediaparser.mp3.enableIndexSeeking"; 689 /** 690 * Sets the operation mode for TS parsing. {@code String} expected. Valid values are {@code 691 * "single_pmt"}, {@code "multi_pmt"}, and {@code "hls"}. Default value is {@code "single_pmt"}. 692 * 693 * <p>The operation modes alter the way TS behaves so that it can handle certain kinds of 694 * commonly-occurring malformed media. 695 * 696 * <ul> 697 * <li>{@code "single_pmt"}: Only the first found PMT is parsed. Others are ignored, even if 698 * more PMTs are declared in the PAT. 699 * <li>{@code "multi_pmt"}: Behave as described in ISO/IEC 13818-1. 700 * <li>{@code "hls"}: Enable {@code "single_pmt"} mode, and ignore continuity counters. 701 * </ul> 702 */ 703 public static final String PARAMETER_TS_MODE = "android.media.mediaparser.ts.mode"; 704 /** 705 * Sets whether TS should treat samples consisting of non-IDR I slices as synchronization 706 * samples (key-frames). {@code boolean} expected. Default value is {@code false}. 707 */ 708 public static final String PARAMETER_TS_ALLOW_NON_IDR_AVC_KEYFRAMES = 709 "android.media.mediaparser.ts.allowNonIdrAvcKeyframes"; 710 /** 711 * Sets whether TS parsing should ignore AAC elementary streams. {@code boolean} expected. 712 * Default value is {@code false}. 713 */ 714 public static final String PARAMETER_TS_IGNORE_AAC_STREAM = 715 "android.media.mediaparser.ts.ignoreAacStream"; 716 /** 717 * Sets whether TS parsing should ignore AVC elementary streams. {@code boolean} expected. 718 * Default value is {@code false}. 719 */ 720 public static final String PARAMETER_TS_IGNORE_AVC_STREAM = 721 "android.media.mediaparser.ts.ignoreAvcStream"; 722 /** 723 * Sets whether TS parsing should ignore splice information streams. {@code boolean} expected. 724 * Default value is {@code false}. 725 */ 726 public static final String PARAMETER_TS_IGNORE_SPLICE_INFO_STREAM = 727 "android.media.mediaparser.ts.ignoreSpliceInfoStream"; 728 /** 729 * Sets whether TS parsing should split AVC stream into access units based on slice headers. 730 * {@code boolean} expected. Default value is {@code false}. 731 * 732 * <p>This flag should be left disabled if the stream contains access units delimiters in order 733 * to avoid unnecessary computational costs. 734 */ 735 public static final String PARAMETER_TS_DETECT_ACCESS_UNITS = 736 "android.media.mediaparser.ts.ignoreDetectAccessUnits"; 737 /** 738 * Sets whether TS parsing should handle HDMV DTS audio streams. {@code boolean} expected. 739 * Default value is {@code false}. 740 * 741 * <p>Enabling this flag will disable the detection of SCTE subtitles. 742 */ 743 public static final String PARAMETER_TS_ENABLE_HDMV_DTS_AUDIO_STREAMS = 744 "android.media.mediaparser.ts.enableHdmvDtsAudioStreams"; 745 /** 746 * Sets whether encryption data should be sent in-band with the sample data, as per {@link 747 * OutputConsumer#onSampleDataFound}. {@code boolean} expected. Default value is {@code false}. 748 * 749 * <p>If this parameter is set, encrypted samples' data will be prefixed with the encryption 750 * information bytes. The format for in-band encryption information is: 751 * 752 * <ul> 753 * <li>(1 byte) {@code encryption_signal_byte}: Most significant bit signals whether the 754 * encryption data contains subsample encryption data. The remaining bits contain {@code 755 * initialization_vector_size}. 756 * <li>({@code initialization_vector_size} bytes) Initialization vector. 757 * <li>If subsample encryption data is present, as per {@code encryption_signal_byte}, the 758 * encryption data also contains: 759 * <ul> 760 * <li>(2 bytes) {@code subsample_encryption_data_length}. 761 * <li>({@code subsample_encryption_data_length * 6} bytes) Subsample encryption data 762 * (repeated {@code subsample_encryption_data_length} times): 763 * <ul> 764 * <li>(2 bytes) Size of a clear section in sample. 765 * <li>(4 bytes) Size of an encryption section in sample. 766 * </ul> 767 * </ul> 768 * </ul> 769 * 770 * @hide 771 */ 772 public static final String PARAMETER_IN_BAND_CRYPTO_INFO = 773 "android.media.mediaparser.inBandCryptoInfo"; 774 /** 775 * Sets whether supplemental data should be included as part of the sample data. {@code boolean} 776 * expected. Default value is {@code false}. See {@link #SAMPLE_FLAG_HAS_SUPPLEMENTAL_DATA} for 777 * information about the sample data format. 778 * 779 * @hide 780 */ 781 public static final String PARAMETER_INCLUDE_SUPPLEMENTAL_DATA = 782 "android.media.mediaparser.includeSupplementalData"; 783 /** 784 * Sets whether sample timestamps may start from non-zero offsets. {@code boolean} expected. 785 * Default value is {@code false}. 786 * 787 * <p>When set to true, sample timestamps will not be offset to start from zero, and the media 788 * provided timestamps will be used instead. For example, transport stream sample timestamps 789 * will not be converted to a zero-based timebase. 790 * 791 * @hide 792 */ 793 public static final String PARAMETER_IGNORE_TIMESTAMP_OFFSET = 794 "android.media.mediaparser.ignoreTimestampOffset"; 795 /** 796 * Sets whether each track type should be eagerly exposed. {@code boolean} expected. Default 797 * value is {@code false}. 798 * 799 * <p>When set to true, each track type will be eagerly exposed through a call to {@link 800 * OutputConsumer#onTrackDataFound} containing a single-value {@link MediaFormat}. The key for 801 * the track type is {@code "track-type-string"}, and the possible values are {@code "video"}, 802 * {@code "audio"}, {@code "text"}, {@code "metadata"}, and {@code "unknown"}. 803 * 804 * @hide 805 */ 806 public static final String PARAMETER_EAGERLY_EXPOSE_TRACKTYPE = 807 "android.media.mediaparser.eagerlyExposeTrackType"; 808 /** 809 * Sets whether a dummy {@link SeekMap} should be exposed before starting extraction. {@code 810 * boolean} expected. Default value is {@code false}. 811 * 812 * <p>For each {@link SeekMap#getSeekPoints} call, the dummy {@link SeekMap} returns a single 813 * {@link SeekPoint} whose {@link SeekPoint#timeMicros} matches the requested timestamp, and 814 * whose {@link SeekPoint#position} is 0. 815 * 816 * @hide 817 */ 818 public static final String PARAMETER_EXPOSE_DUMMY_SEEKMAP = 819 "android.media.mediaparser.exposeDummySeekMap"; 820 821 /** 822 * Sets whether chunk indices available in the extracted media should be exposed as {@link 823 * MediaFormat MediaFormats}. {@code boolean} expected. Default value is {@link false}. 824 * 825 * <p>When set to true, any information about media segmentation will be exposed as a {@link 826 * MediaFormat} (with track index 0) containing four {@link ByteBuffer} elements under the 827 * following keys: 828 * 829 * <ul> 830 * <li>"chunk-index-int-sizes": Contains {@code ints} representing the sizes in bytes of each 831 * of the media segments. 832 * <li>"chunk-index-long-offsets": Contains {@code longs} representing the byte offsets of 833 * each segment in the stream. 834 * <li>"chunk-index-long-us-durations": Contains {@code longs} representing the media duration 835 * of each segment, in microseconds. 836 * <li>"chunk-index-long-us-times": Contains {@code longs} representing the start time of each 837 * segment, in microseconds. 838 * </ul> 839 * 840 * @hide 841 */ 842 public static final String PARAMETER_EXPOSE_CHUNK_INDEX_AS_MEDIA_FORMAT = 843 "android.media.mediaParser.exposeChunkIndexAsMediaFormat"; 844 /** 845 * Sets a list of closed-caption {@link MediaFormat MediaFormats} that should be exposed as part 846 * of the extracted media. {@code List<MediaFormat>} expected. Default value is an empty list. 847 * 848 * <p>Expected keys in the {@link MediaFormat} are: 849 * 850 * <ul> 851 * <p>{@link MediaFormat#KEY_MIME}: Determine the type of captions (for example, 852 * application/cea-608). Mandatory. 853 * <p>{@link MediaFormat#KEY_CAPTION_SERVICE_NUMBER}: Determine the channel on which the 854 * captions are transmitted. Optional. 855 * </ul> 856 * 857 * @hide 858 */ 859 public static final String PARAMETER_EXPOSE_CAPTION_FORMATS = 860 "android.media.mediaParser.exposeCaptionFormats"; 861 /** 862 * Sets whether the value associated with {@link #PARAMETER_EXPOSE_CAPTION_FORMATS} should 863 * override any in-band caption service declarations. {@code boolean} expected. Default value is 864 * {@link false}. 865 * 866 * <p>When {@code false}, any present in-band caption services information will override the 867 * values associated with {@link #PARAMETER_EXPOSE_CAPTION_FORMATS}. 868 * 869 * @hide 870 */ 871 public static final String PARAMETER_OVERRIDE_IN_BAND_CAPTION_DECLARATIONS = 872 "android.media.mediaParser.overrideInBandCaptionDeclarations"; 873 /** 874 * Sets whether a track for EMSG events should be exposed in case of parsing a container that 875 * supports them. {@code boolean} expected. Default value is {@link false}. 876 * 877 * @hide 878 */ 879 public static final String PARAMETER_EXPOSE_EMSG_TRACK = 880 "android.media.mediaParser.exposeEmsgTrack"; 881 882 // Private constants. 883 884 private static final String TAG = "MediaParser"; 885 private static final Map<String, ExtractorFactory> EXTRACTOR_FACTORIES_BY_NAME; 886 private static final Map<String, Class> EXPECTED_TYPE_BY_PARAMETER_NAME; 887 private static final String TS_MODE_SINGLE_PMT = "single_pmt"; 888 private static final String TS_MODE_MULTI_PMT = "multi_pmt"; 889 private static final String TS_MODE_HLS = "hls"; 890 private static final int BYTES_PER_SUBSAMPLE_ENCRYPTION_ENTRY = 6; 891 private static final byte[] EMPTY_BYTE_ARRAY = new byte[0]; 892 893 @IntDef( 894 value = { 895 STATE_READING_SIGNAL_BYTE, 896 STATE_READING_INIT_VECTOR, 897 STATE_READING_SUBSAMPLE_ENCRYPTION_SIZE, 898 STATE_READING_SUBSAMPLE_ENCRYPTION_DATA 899 }) 900 private @interface EncryptionDataReadState {} 901 902 private static final int STATE_READING_SIGNAL_BYTE = 0; 903 private static final int STATE_READING_INIT_VECTOR = 1; 904 private static final int STATE_READING_SUBSAMPLE_ENCRYPTION_SIZE = 2; 905 private static final int STATE_READING_SUBSAMPLE_ENCRYPTION_DATA = 3; 906 907 // Instance creation methods. 908 909 /** 910 * Creates an instance backed by the parser with the given {@code name}. The returned instance 911 * will attempt parsing without sniffing the content. 912 * 913 * @param name The name of the parser that will be associated with the created instance. 914 * @param outputConsumer The {@link OutputConsumer} to which track data and samples are pushed. 915 * @return A new instance. 916 * @throws IllegalArgumentException If an invalid name is provided. 917 */ 918 @NonNull createByName( @onNull @arserName String name, @NonNull OutputConsumer outputConsumer)919 public static MediaParser createByName( 920 @NonNull @ParserName String name, @NonNull OutputConsumer outputConsumer) { 921 String[] nameAsArray = new String[] {name}; 922 assertValidNames(nameAsArray); 923 return new MediaParser(outputConsumer, /* sniff= */ false, name); 924 } 925 926 /** 927 * Creates an instance whose backing parser will be selected by sniffing the content during the 928 * first {@link #advance} call. Parser implementations will sniff the content in order of 929 * appearance in {@code parserNames}. 930 * 931 * @param outputConsumer The {@link OutputConsumer} to which extracted data is output. 932 * @param parserNames The names of the parsers to sniff the content with. If empty, a default 933 * array of names is used. 934 * @return A new instance. 935 */ 936 @NonNull create( @onNull OutputConsumer outputConsumer, @NonNull @ParserName String... parserNames)937 public static MediaParser create( 938 @NonNull OutputConsumer outputConsumer, @NonNull @ParserName String... parserNames) { 939 assertValidNames(parserNames); 940 if (parserNames.length == 0) { 941 parserNames = EXTRACTOR_FACTORIES_BY_NAME.keySet().toArray(new String[0]); 942 } 943 return new MediaParser(outputConsumer, /* sniff= */ true, parserNames); 944 } 945 946 // Misc static methods. 947 948 /** 949 * Returns an immutable list with the names of the parsers that are suitable for container 950 * formats with the given {@link MediaFormat}. 951 * 952 * <p>A parser supports a {@link MediaFormat} if the mime type associated with {@link 953 * MediaFormat#KEY_MIME} corresponds to the supported container format. 954 * 955 * @param mediaFormat The {@link MediaFormat} to check support for. 956 * @return The parser names that support the given {@code mediaFormat}, or the list of all 957 * parsers available if no container specific format information is provided. 958 */ 959 @NonNull 960 @ParserName getParserNames(@onNull MediaFormat mediaFormat)961 public static List<String> getParserNames(@NonNull MediaFormat mediaFormat) { 962 String mimeType = mediaFormat.getString(MediaFormat.KEY_MIME); 963 mimeType = mimeType == null ? null : Util.toLowerInvariant(mimeType.trim()); 964 if (TextUtils.isEmpty(mimeType)) { 965 // No MIME type provided. Return all. 966 return Collections.unmodifiableList( 967 new ArrayList<>(EXTRACTOR_FACTORIES_BY_NAME.keySet())); 968 } 969 ArrayList<String> result = new ArrayList<>(); 970 switch (mimeType) { 971 case "video/x-matroska": 972 case "audio/x-matroska": 973 case "video/x-webm": 974 case "audio/x-webm": 975 result.add(PARSER_NAME_MATROSKA); 976 break; 977 case "video/mp4": 978 case "audio/mp4": 979 case "application/mp4": 980 result.add(PARSER_NAME_MP4); 981 result.add(PARSER_NAME_FMP4); 982 break; 983 case "audio/mpeg": 984 result.add(PARSER_NAME_MP3); 985 break; 986 case "audio/aac": 987 result.add(PARSER_NAME_ADTS); 988 break; 989 case "audio/ac3": 990 result.add(PARSER_NAME_AC3); 991 break; 992 case "video/mp2t": 993 case "audio/mp2t": 994 result.add(PARSER_NAME_TS); 995 break; 996 case "video/x-flv": 997 result.add(PARSER_NAME_FLV); 998 break; 999 case "video/ogg": 1000 case "audio/ogg": 1001 case "application/ogg": 1002 result.add(PARSER_NAME_OGG); 1003 break; 1004 case "video/mp2p": 1005 case "video/mp1s": 1006 result.add(PARSER_NAME_PS); 1007 break; 1008 case "audio/vnd.wave": 1009 case "audio/wav": 1010 case "audio/wave": 1011 case "audio/x-wav": 1012 result.add(PARSER_NAME_WAV); 1013 break; 1014 case "audio/amr": 1015 result.add(PARSER_NAME_AMR); 1016 break; 1017 case "audio/ac4": 1018 result.add(PARSER_NAME_AC4); 1019 break; 1020 case "audio/flac": 1021 case "audio/x-flac": 1022 result.add(PARSER_NAME_FLAC); 1023 break; 1024 default: 1025 // No parsers support the given mime type. Do nothing. 1026 break; 1027 } 1028 return Collections.unmodifiableList(result); 1029 } 1030 1031 // Private fields. 1032 1033 private final Map<String, Object> mParserParameters; 1034 private final OutputConsumer mOutputConsumer; 1035 private final String[] mParserNamesPool; 1036 private final PositionHolder mPositionHolder; 1037 private final InputReadingDataReader mExoDataReader; 1038 private final DataReaderAdapter mScratchDataReaderAdapter; 1039 private final ParsableByteArrayAdapter mScratchParsableByteArrayAdapter; 1040 @Nullable private final Constructor<DrmInitData.SchemeInitData> mSchemeInitDataConstructor; 1041 private final ArrayList<Format> mMuxedCaptionFormats; 1042 private boolean mInBandCryptoInfo; 1043 private boolean mIncludeSupplementalData; 1044 private boolean mIgnoreTimestampOffset; 1045 private boolean mEagerlyExposeTrackType; 1046 private boolean mExposeDummySeekMap; 1047 private boolean mExposeChunkIndexAsMediaFormat; 1048 private String mParserName; 1049 private Extractor mExtractor; 1050 private ExtractorInput mExtractorInput; 1051 private boolean mPendingExtractorInit; 1052 private long mPendingSeekPosition; 1053 private long mPendingSeekTimeMicros; 1054 private boolean mLoggedSchemeInitDataCreationException; 1055 1056 // Public methods. 1057 1058 /** 1059 * Sets parser-specific parameters which allow customizing behavior. 1060 * 1061 * <p>Must be called before the first call to {@link #advance}. 1062 * 1063 * @param parameterName The name of the parameter to set. See {@code PARAMETER_*} constants for 1064 * documentation on possible values. 1065 * @param value The value to set for the given {@code parameterName}. See {@code PARAMETER_*} 1066 * constants for documentation on the expected types. 1067 * @return This instance, for convenience. 1068 * @throws IllegalStateException If called after calling {@link #advance} on the same instance. 1069 */ 1070 @NonNull setParameter( @onNull @arameterName String parameterName, @NonNull Object value)1071 public MediaParser setParameter( 1072 @NonNull @ParameterName String parameterName, @NonNull Object value) { 1073 if (mExtractor != null) { 1074 throw new IllegalStateException( 1075 "setParameters() must be called before the first advance() call."); 1076 } 1077 Class expectedType = EXPECTED_TYPE_BY_PARAMETER_NAME.get(parameterName); 1078 // Ignore parameter names that are not contained in the map, in case the client is passing 1079 // a parameter that is being added in a future version of this library. 1080 if (expectedType != null && !expectedType.isInstance(value)) { 1081 throw new IllegalArgumentException( 1082 parameterName 1083 + " expects a " 1084 + expectedType.getSimpleName() 1085 + " but a " 1086 + value.getClass().getSimpleName() 1087 + " was passed."); 1088 } 1089 if (PARAMETER_TS_MODE.equals(parameterName) 1090 && !TS_MODE_SINGLE_PMT.equals(value) 1091 && !TS_MODE_HLS.equals(value) 1092 && !TS_MODE_MULTI_PMT.equals(value)) { 1093 throw new IllegalArgumentException(PARAMETER_TS_MODE + " does not accept: " + value); 1094 } 1095 if (PARAMETER_IN_BAND_CRYPTO_INFO.equals(parameterName)) { 1096 mInBandCryptoInfo = (boolean) value; 1097 } 1098 if (PARAMETER_INCLUDE_SUPPLEMENTAL_DATA.equals(parameterName)) { 1099 mIncludeSupplementalData = (boolean) value; 1100 } 1101 if (PARAMETER_IGNORE_TIMESTAMP_OFFSET.equals(parameterName)) { 1102 mIgnoreTimestampOffset = (boolean) value; 1103 } 1104 if (PARAMETER_EAGERLY_EXPOSE_TRACKTYPE.equals(parameterName)) { 1105 mEagerlyExposeTrackType = (boolean) value; 1106 } 1107 if (PARAMETER_EXPOSE_DUMMY_SEEKMAP.equals(parameterName)) { 1108 mExposeDummySeekMap = (boolean) value; 1109 } 1110 if (PARAMETER_EXPOSE_CHUNK_INDEX_AS_MEDIA_FORMAT.equals(parameterName)) { 1111 mExposeChunkIndexAsMediaFormat = (boolean) value; 1112 } 1113 if (PARAMETER_EXPOSE_CAPTION_FORMATS.equals(parameterName)) { 1114 setMuxedCaptionFormats((List<MediaFormat>) value); 1115 } 1116 mParserParameters.put(parameterName, value); 1117 return this; 1118 } 1119 1120 /** 1121 * Returns whether the given {@code parameterName} is supported by this parser. 1122 * 1123 * @param parameterName The parameter name to check support for. One of the {@code PARAMETER_*} 1124 * constants. 1125 * @return Whether the given {@code parameterName} is supported. 1126 */ supportsParameter(@onNull @arameterName String parameterName)1127 public boolean supportsParameter(@NonNull @ParameterName String parameterName) { 1128 return EXPECTED_TYPE_BY_PARAMETER_NAME.containsKey(parameterName); 1129 } 1130 1131 /** 1132 * Returns the name of the backing parser implementation. 1133 * 1134 * <p>If this instance was creating using {@link #createByName}, the provided name is returned. 1135 * If this instance was created using {@link #create}, this method will return {@link 1136 * #PARSER_NAME_UNKNOWN} until the first call to {@link #advance}, after which the name of the 1137 * backing parser implementation is returned. 1138 * 1139 * @return The name of the backing parser implementation, or null if the backing parser 1140 * implementation has not yet been selected. 1141 */ 1142 @NonNull 1143 @ParserName getParserName()1144 public String getParserName() { 1145 return mParserName; 1146 } 1147 1148 /** 1149 * Makes progress in the extraction of the input media stream, unless the end of the input has 1150 * been reached. 1151 * 1152 * <p>This method will block until some progress has been made. 1153 * 1154 * <p>If this instance was created using {@link #create}, the first call to this method will 1155 * sniff the content using the selected parser implementations. 1156 * 1157 * @param seekableInputReader The {@link SeekableInputReader} from which to obtain the media 1158 * container data. 1159 * @return Whether there is any data left to extract. Returns false if the end of input has been 1160 * reached. 1161 * @throws IOException If an error occurs while reading from the {@link SeekableInputReader}. 1162 * @throws UnrecognizedInputFormatException If the format cannot be recognized by any of the 1163 * underlying parser implementations. 1164 */ advance(@onNull SeekableInputReader seekableInputReader)1165 public boolean advance(@NonNull SeekableInputReader seekableInputReader) throws IOException { 1166 if (mExtractorInput == null) { 1167 // TODO: For efficiency, the same implementation should be used, by providing a 1168 // clearBuffers() method, or similar. 1169 mExtractorInput = 1170 new DefaultExtractorInput( 1171 mExoDataReader, 1172 seekableInputReader.getPosition(), 1173 seekableInputReader.getLength()); 1174 } 1175 mExoDataReader.mInputReader = seekableInputReader; 1176 1177 if (mExtractor == null) { 1178 mPendingExtractorInit = true; 1179 if (!mParserName.equals(PARSER_NAME_UNKNOWN)) { 1180 mExtractor = createExtractor(mParserName); 1181 } else { 1182 for (String parserName : mParserNamesPool) { 1183 Extractor extractor = createExtractor(parserName); 1184 try { 1185 if (extractor.sniff(mExtractorInput)) { 1186 mParserName = parserName; 1187 mExtractor = extractor; 1188 mPendingExtractorInit = true; 1189 break; 1190 } 1191 } catch (EOFException e) { 1192 // Do nothing. 1193 } finally { 1194 mExtractorInput.resetPeekPosition(); 1195 } 1196 } 1197 if (mExtractor == null) { 1198 throw UnrecognizedInputFormatException.createForExtractors(mParserNamesPool); 1199 } 1200 return true; 1201 } 1202 } 1203 1204 if (mPendingExtractorInit) { 1205 if (mExposeDummySeekMap) { 1206 // We propagate the dummy seek map before initializing the extractor, in case the 1207 // extractor initialization outputs a seek map. 1208 mOutputConsumer.onSeekMapFound(SeekMap.DUMMY); 1209 } 1210 mExtractor.init(new ExtractorOutputAdapter()); 1211 mPendingExtractorInit = false; 1212 // We return after initialization to allow clients use any output information before 1213 // starting actual extraction. 1214 return true; 1215 } 1216 1217 if (isPendingSeek()) { 1218 mExtractor.seek(mPendingSeekPosition, mPendingSeekTimeMicros); 1219 removePendingSeek(); 1220 } 1221 1222 mPositionHolder.position = seekableInputReader.getPosition(); 1223 int result; 1224 try { 1225 result = mExtractor.read(mExtractorInput, mPositionHolder); 1226 } catch (ParserException e) { 1227 throw new ParsingException(e); 1228 } 1229 if (result == Extractor.RESULT_END_OF_INPUT) { 1230 mExtractorInput = null; 1231 return false; 1232 } 1233 if (result == Extractor.RESULT_SEEK) { 1234 mExtractorInput = null; 1235 seekableInputReader.seekToPosition(mPositionHolder.position); 1236 } 1237 return true; 1238 } 1239 1240 /** 1241 * Seeks within the media container being extracted. 1242 * 1243 * <p>{@link SeekPoint SeekPoints} can be obtained from the {@link SeekMap} passed to {@link 1244 * OutputConsumer#onSeekMapFound(SeekMap)}. 1245 * 1246 * <p>Following a call to this method, the {@link InputReader} passed to the next invocation of 1247 * {@link #advance} must provide data starting from {@link SeekPoint#position} in the stream. 1248 * 1249 * @param seekPoint The {@link SeekPoint} to seek to. 1250 */ seek(@onNull SeekPoint seekPoint)1251 public void seek(@NonNull SeekPoint seekPoint) { 1252 if (mExtractor == null) { 1253 mPendingSeekPosition = seekPoint.position; 1254 mPendingSeekTimeMicros = seekPoint.timeMicros; 1255 } else { 1256 mExtractor.seek(seekPoint.position, seekPoint.timeMicros); 1257 } 1258 } 1259 1260 /** 1261 * Releases any acquired resources. 1262 * 1263 * <p>After calling this method, this instance becomes unusable and no other methods should be 1264 * invoked. 1265 */ release()1266 public void release() { 1267 // TODO: Dump media metrics here. 1268 mExtractorInput = null; 1269 mExtractor = null; 1270 } 1271 1272 // Private methods. 1273 MediaParser(OutputConsumer outputConsumer, boolean sniff, String... parserNamesPool)1274 private MediaParser(OutputConsumer outputConsumer, boolean sniff, String... parserNamesPool) { 1275 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) { 1276 throw new UnsupportedOperationException("Android version must be R or greater."); 1277 } 1278 mParserParameters = new HashMap<>(); 1279 mOutputConsumer = outputConsumer; 1280 mParserNamesPool = parserNamesPool; 1281 mParserName = sniff ? PARSER_NAME_UNKNOWN : parserNamesPool[0]; 1282 mPositionHolder = new PositionHolder(); 1283 mExoDataReader = new InputReadingDataReader(); 1284 removePendingSeek(); 1285 mScratchDataReaderAdapter = new DataReaderAdapter(); 1286 mScratchParsableByteArrayAdapter = new ParsableByteArrayAdapter(); 1287 mSchemeInitDataConstructor = getSchemeInitDataConstructor(); 1288 mMuxedCaptionFormats = new ArrayList<>(); 1289 } 1290 setMuxedCaptionFormats(List<MediaFormat> mediaFormats)1291 private void setMuxedCaptionFormats(List<MediaFormat> mediaFormats) { 1292 mMuxedCaptionFormats.clear(); 1293 for (MediaFormat mediaFormat : mediaFormats) { 1294 mMuxedCaptionFormats.add(toExoPlayerCaptionFormat(mediaFormat)); 1295 } 1296 } 1297 isPendingSeek()1298 private boolean isPendingSeek() { 1299 return mPendingSeekPosition >= 0; 1300 } 1301 removePendingSeek()1302 private void removePendingSeek() { 1303 mPendingSeekPosition = -1; 1304 mPendingSeekTimeMicros = -1; 1305 } 1306 createExtractor(String parserName)1307 private Extractor createExtractor(String parserName) { 1308 int flags = 0; 1309 TimestampAdjuster timestampAdjuster = null; 1310 if (mIgnoreTimestampOffset) { 1311 timestampAdjuster = new TimestampAdjuster(TimestampAdjuster.DO_NOT_OFFSET); 1312 } 1313 switch (parserName) { 1314 case PARSER_NAME_MATROSKA: 1315 flags = 1316 getBooleanParameter(PARAMETER_MATROSKA_DISABLE_CUES_SEEKING) 1317 ? MatroskaExtractor.FLAG_DISABLE_SEEK_FOR_CUES 1318 : 0; 1319 return new MatroskaExtractor(flags); 1320 case PARSER_NAME_FMP4: 1321 flags |= 1322 getBooleanParameter(PARAMETER_EXPOSE_EMSG_TRACK) 1323 ? FragmentedMp4Extractor.FLAG_ENABLE_EMSG_TRACK 1324 : 0; 1325 flags |= 1326 getBooleanParameter(PARAMETER_MP4_IGNORE_EDIT_LISTS) 1327 ? FragmentedMp4Extractor.FLAG_WORKAROUND_IGNORE_EDIT_LISTS 1328 : 0; 1329 flags |= 1330 getBooleanParameter(PARAMETER_MP4_IGNORE_TFDT_BOX) 1331 ? FragmentedMp4Extractor.FLAG_WORKAROUND_IGNORE_TFDT_BOX 1332 : 0; 1333 flags |= 1334 getBooleanParameter(PARAMETER_MP4_TREAT_VIDEO_FRAMES_AS_KEYFRAMES) 1335 ? FragmentedMp4Extractor 1336 .FLAG_WORKAROUND_EVERY_VIDEO_FRAME_IS_SYNC_FRAME 1337 : 0; 1338 return new FragmentedMp4Extractor( 1339 flags, 1340 timestampAdjuster, 1341 /* sideloadedTrack= */ null, 1342 mMuxedCaptionFormats); 1343 case PARSER_NAME_MP4: 1344 flags |= 1345 getBooleanParameter(PARAMETER_MP4_IGNORE_EDIT_LISTS) 1346 ? Mp4Extractor.FLAG_WORKAROUND_IGNORE_EDIT_LISTS 1347 : 0; 1348 return new Mp4Extractor(flags); 1349 case PARSER_NAME_MP3: 1350 flags |= 1351 getBooleanParameter(PARAMETER_MP3_DISABLE_ID3) 1352 ? Mp3Extractor.FLAG_DISABLE_ID3_METADATA 1353 : 0; 1354 flags |= 1355 getBooleanParameter(PARAMETER_MP3_ENABLE_CBR_SEEKING) 1356 ? Mp3Extractor.FLAG_ENABLE_CONSTANT_BITRATE_SEEKING 1357 : 0; 1358 // TODO: Add index seeking once we update the ExoPlayer version. 1359 return new Mp3Extractor(flags); 1360 case PARSER_NAME_ADTS: 1361 flags |= 1362 getBooleanParameter(PARAMETER_ADTS_ENABLE_CBR_SEEKING) 1363 ? AdtsExtractor.FLAG_ENABLE_CONSTANT_BITRATE_SEEKING 1364 : 0; 1365 return new AdtsExtractor(flags); 1366 case PARSER_NAME_AC3: 1367 return new Ac3Extractor(); 1368 case PARSER_NAME_TS: 1369 flags |= 1370 getBooleanParameter(PARAMETER_TS_ALLOW_NON_IDR_AVC_KEYFRAMES) 1371 ? DefaultTsPayloadReaderFactory.FLAG_ALLOW_NON_IDR_KEYFRAMES 1372 : 0; 1373 flags |= 1374 getBooleanParameter(PARAMETER_TS_DETECT_ACCESS_UNITS) 1375 ? DefaultTsPayloadReaderFactory.FLAG_DETECT_ACCESS_UNITS 1376 : 0; 1377 flags |= 1378 getBooleanParameter(PARAMETER_TS_ENABLE_HDMV_DTS_AUDIO_STREAMS) 1379 ? DefaultTsPayloadReaderFactory.FLAG_ENABLE_HDMV_DTS_AUDIO_STREAMS 1380 : 0; 1381 flags |= 1382 getBooleanParameter(PARAMETER_TS_IGNORE_AAC_STREAM) 1383 ? DefaultTsPayloadReaderFactory.FLAG_IGNORE_AAC_STREAM 1384 : 0; 1385 flags |= 1386 getBooleanParameter(PARAMETER_TS_IGNORE_AVC_STREAM) 1387 ? DefaultTsPayloadReaderFactory.FLAG_IGNORE_H264_STREAM 1388 : 0; 1389 flags |= 1390 getBooleanParameter(PARAMETER_TS_IGNORE_SPLICE_INFO_STREAM) 1391 ? DefaultTsPayloadReaderFactory.FLAG_IGNORE_SPLICE_INFO_STREAM 1392 : 0; 1393 flags |= 1394 getBooleanParameter(PARAMETER_OVERRIDE_IN_BAND_CAPTION_DECLARATIONS) 1395 ? DefaultTsPayloadReaderFactory.FLAG_OVERRIDE_CAPTION_DESCRIPTORS 1396 : 0; 1397 String tsMode = getStringParameter(PARAMETER_TS_MODE, TS_MODE_SINGLE_PMT); 1398 int hlsMode = 1399 TS_MODE_SINGLE_PMT.equals(tsMode) 1400 ? TsExtractor.MODE_SINGLE_PMT 1401 : TS_MODE_HLS.equals(tsMode) 1402 ? TsExtractor.MODE_HLS 1403 : TsExtractor.MODE_MULTI_PMT; 1404 return new TsExtractor( 1405 hlsMode, 1406 timestampAdjuster != null 1407 ? timestampAdjuster 1408 : new TimestampAdjuster(/* firstSampleTimestampUs= */ 0), 1409 new DefaultTsPayloadReaderFactory(flags, mMuxedCaptionFormats)); 1410 case PARSER_NAME_FLV: 1411 return new FlvExtractor(); 1412 case PARSER_NAME_OGG: 1413 return new OggExtractor(); 1414 case PARSER_NAME_PS: 1415 return new PsExtractor(); 1416 case PARSER_NAME_WAV: 1417 return new WavExtractor(); 1418 case PARSER_NAME_AMR: 1419 flags |= 1420 getBooleanParameter(PARAMETER_AMR_ENABLE_CBR_SEEKING) 1421 ? AmrExtractor.FLAG_ENABLE_CONSTANT_BITRATE_SEEKING 1422 : 0; 1423 return new AmrExtractor(flags); 1424 case PARSER_NAME_AC4: 1425 return new Ac4Extractor(); 1426 case PARSER_NAME_FLAC: 1427 flags |= 1428 getBooleanParameter(PARAMETER_FLAC_DISABLE_ID3) 1429 ? FlacExtractor.FLAG_DISABLE_ID3_METADATA 1430 : 0; 1431 return new FlacExtractor(flags); 1432 default: 1433 // Should never happen. 1434 throw new IllegalStateException("Unexpected attempt to create: " + parserName); 1435 } 1436 } 1437 getBooleanParameter(String name)1438 private boolean getBooleanParameter(String name) { 1439 return (boolean) mParserParameters.getOrDefault(name, false); 1440 } 1441 getStringParameter(String name, String defaultValue)1442 private String getStringParameter(String name, String defaultValue) { 1443 return (String) mParserParameters.getOrDefault(name, defaultValue); 1444 } 1445 1446 // Private classes. 1447 1448 private static final class InputReadingDataReader implements DataReader { 1449 1450 public InputReader mInputReader; 1451 1452 @Override read(byte[] buffer, int offset, int readLength)1453 public int read(byte[] buffer, int offset, int readLength) throws IOException { 1454 return mInputReader.read(buffer, offset, readLength); 1455 } 1456 } 1457 1458 private final class MediaParserDrmInitData extends DrmInitData { 1459 1460 private final SchemeInitData[] mSchemeDatas; 1461 MediaParserDrmInitData(com.google.android.exoplayer2.drm.DrmInitData exoDrmInitData)1462 private MediaParserDrmInitData(com.google.android.exoplayer2.drm.DrmInitData exoDrmInitData) 1463 throws IllegalAccessException, InstantiationException, InvocationTargetException { 1464 mSchemeDatas = new SchemeInitData[exoDrmInitData.schemeDataCount]; 1465 for (int i = 0; i < mSchemeDatas.length; i++) { 1466 mSchemeDatas[i] = toFrameworkSchemeInitData(exoDrmInitData.get(i)); 1467 } 1468 } 1469 1470 @Override 1471 @Nullable get(UUID schemeUuid)1472 public SchemeInitData get(UUID schemeUuid) { 1473 for (SchemeInitData schemeInitData : mSchemeDatas) { 1474 if (schemeInitData.uuid.equals(schemeUuid)) { 1475 return schemeInitData; 1476 } 1477 } 1478 return null; 1479 } 1480 1481 @Override getSchemeInitDataAt(int index)1482 public SchemeInitData getSchemeInitDataAt(int index) { 1483 return mSchemeDatas[index]; 1484 } 1485 1486 @Override getSchemeInitDataCount()1487 public int getSchemeInitDataCount() { 1488 return mSchemeDatas.length; 1489 } 1490 toFrameworkSchemeInitData(SchemeData exoSchemeData)1491 private DrmInitData.SchemeInitData toFrameworkSchemeInitData(SchemeData exoSchemeData) 1492 throws IllegalAccessException, InvocationTargetException, InstantiationException { 1493 return mSchemeInitDataConstructor.newInstance( 1494 exoSchemeData.uuid, exoSchemeData.mimeType, exoSchemeData.data); 1495 } 1496 } 1497 1498 private final class ExtractorOutputAdapter implements ExtractorOutput { 1499 1500 private final SparseArray<TrackOutput> mTrackOutputAdapters; 1501 private boolean mTracksEnded; 1502 ExtractorOutputAdapter()1503 private ExtractorOutputAdapter() { 1504 mTrackOutputAdapters = new SparseArray<>(); 1505 } 1506 1507 @Override track(int id, int type)1508 public TrackOutput track(int id, int type) { 1509 TrackOutput trackOutput = mTrackOutputAdapters.get(id); 1510 if (trackOutput == null) { 1511 int trackIndex = mTrackOutputAdapters.size(); 1512 trackOutput = new TrackOutputAdapter(trackIndex); 1513 mTrackOutputAdapters.put(id, trackOutput); 1514 if (mEagerlyExposeTrackType) { 1515 MediaFormat mediaFormat = new MediaFormat(); 1516 mediaFormat.setString("track-type-string", toTypeString(type)); 1517 mOutputConsumer.onTrackDataFound( 1518 trackIndex, new TrackData(mediaFormat, /* drmInitData= */ null)); 1519 } 1520 } 1521 return trackOutput; 1522 } 1523 1524 @Override endTracks()1525 public void endTracks() { 1526 mOutputConsumer.onTrackCountFound(mTrackOutputAdapters.size()); 1527 } 1528 1529 @Override seekMap(com.google.android.exoplayer2.extractor.SeekMap exoplayerSeekMap)1530 public void seekMap(com.google.android.exoplayer2.extractor.SeekMap exoplayerSeekMap) { 1531 if (mExposeChunkIndexAsMediaFormat && exoplayerSeekMap instanceof ChunkIndex) { 1532 ChunkIndex chunkIndex = (ChunkIndex) exoplayerSeekMap; 1533 MediaFormat mediaFormat = new MediaFormat(); 1534 mediaFormat.setByteBuffer("chunk-index-int-sizes", toByteBuffer(chunkIndex.sizes)); 1535 mediaFormat.setByteBuffer( 1536 "chunk-index-long-offsets", toByteBuffer(chunkIndex.offsets)); 1537 mediaFormat.setByteBuffer( 1538 "chunk-index-long-us-durations", toByteBuffer(chunkIndex.durationsUs)); 1539 mediaFormat.setByteBuffer( 1540 "chunk-index-long-us-times", toByteBuffer(chunkIndex.timesUs)); 1541 mOutputConsumer.onTrackDataFound( 1542 /* trackIndex= */ 0, new TrackData(mediaFormat, /* drmInitData= */ null)); 1543 } 1544 mOutputConsumer.onSeekMapFound(new SeekMap(exoplayerSeekMap)); 1545 } 1546 } 1547 1548 private class TrackOutputAdapter implements TrackOutput { 1549 1550 private final int mTrackIndex; 1551 1552 private CryptoInfo mLastOutputCryptoInfo; 1553 private CryptoInfo.Pattern mLastOutputEncryptionPattern; 1554 private CryptoData mLastReceivedCryptoData; 1555 1556 @EncryptionDataReadState private int mEncryptionDataReadState; 1557 private int mEncryptionDataSizeToSubtractFromSampleDataSize; 1558 private int mEncryptionVectorSize; 1559 private byte[] mScratchIvSpace; 1560 private int mSubsampleEncryptionDataSize; 1561 private int[] mScratchSubsampleEncryptedBytesCount; 1562 private int[] mScratchSubsampleClearBytesCount; 1563 private boolean mHasSubsampleEncryptionData; 1564 private int mSkippedSupplementalDataBytes; 1565 TrackOutputAdapter(int trackIndex)1566 private TrackOutputAdapter(int trackIndex) { 1567 mTrackIndex = trackIndex; 1568 mScratchIvSpace = new byte[16]; // Size documented in CryptoInfo. 1569 mScratchSubsampleEncryptedBytesCount = new int[32]; 1570 mScratchSubsampleClearBytesCount = new int[32]; 1571 mEncryptionDataReadState = STATE_READING_SIGNAL_BYTE; 1572 mLastOutputEncryptionPattern = 1573 new CryptoInfo.Pattern(/* blocksToEncrypt= */ 0, /* blocksToSkip= */ 0); 1574 } 1575 1576 @Override format(Format format)1577 public void format(Format format) { 1578 mOutputConsumer.onTrackDataFound( 1579 mTrackIndex, 1580 new TrackData( 1581 toMediaFormat(format), toFrameworkDrmInitData(format.drmInitData))); 1582 } 1583 1584 @Override sampleData( DataReader input, int length, boolean allowEndOfInput, @SampleDataPart int sampleDataPart)1585 public int sampleData( 1586 DataReader input, 1587 int length, 1588 boolean allowEndOfInput, 1589 @SampleDataPart int sampleDataPart) 1590 throws IOException { 1591 mScratchDataReaderAdapter.setDataReader(input, length); 1592 long positionBeforeReading = mScratchDataReaderAdapter.getPosition(); 1593 mOutputConsumer.onSampleDataFound(mTrackIndex, mScratchDataReaderAdapter); 1594 return (int) (mScratchDataReaderAdapter.getPosition() - positionBeforeReading); 1595 } 1596 1597 @Override sampleData( ParsableByteArray data, int length, @SampleDataPart int sampleDataPart)1598 public void sampleData( 1599 ParsableByteArray data, int length, @SampleDataPart int sampleDataPart) { 1600 if (sampleDataPart == SAMPLE_DATA_PART_ENCRYPTION && !mInBandCryptoInfo) { 1601 while (length > 0) { 1602 switch (mEncryptionDataReadState) { 1603 case STATE_READING_SIGNAL_BYTE: 1604 int encryptionSignalByte = data.readUnsignedByte(); 1605 length--; 1606 mHasSubsampleEncryptionData = ((encryptionSignalByte >> 7) & 1) != 0; 1607 mEncryptionVectorSize = encryptionSignalByte & 0x7F; 1608 mEncryptionDataSizeToSubtractFromSampleDataSize = 1609 mEncryptionVectorSize + 1; // Signal byte. 1610 mEncryptionDataReadState = STATE_READING_INIT_VECTOR; 1611 break; 1612 case STATE_READING_INIT_VECTOR: 1613 Arrays.fill(mScratchIvSpace, (byte) 0); // Ensure 0-padding. 1614 data.readBytes(mScratchIvSpace, /* offset= */ 0, mEncryptionVectorSize); 1615 length -= mEncryptionVectorSize; 1616 if (mHasSubsampleEncryptionData) { 1617 mEncryptionDataReadState = STATE_READING_SUBSAMPLE_ENCRYPTION_SIZE; 1618 } else { 1619 mSubsampleEncryptionDataSize = 0; 1620 mEncryptionDataReadState = STATE_READING_SIGNAL_BYTE; 1621 } 1622 break; 1623 case STATE_READING_SUBSAMPLE_ENCRYPTION_SIZE: 1624 mSubsampleEncryptionDataSize = data.readUnsignedShort(); 1625 if (mScratchSubsampleClearBytesCount.length 1626 < mSubsampleEncryptionDataSize) { 1627 mScratchSubsampleClearBytesCount = 1628 new int[mSubsampleEncryptionDataSize]; 1629 mScratchSubsampleEncryptedBytesCount = 1630 new int[mSubsampleEncryptionDataSize]; 1631 } 1632 length -= 2; 1633 mEncryptionDataSizeToSubtractFromSampleDataSize += 1634 2 1635 + mSubsampleEncryptionDataSize 1636 * BYTES_PER_SUBSAMPLE_ENCRYPTION_ENTRY; 1637 mEncryptionDataReadState = STATE_READING_SUBSAMPLE_ENCRYPTION_DATA; 1638 break; 1639 case STATE_READING_SUBSAMPLE_ENCRYPTION_DATA: 1640 for (int i = 0; i < mSubsampleEncryptionDataSize; i++) { 1641 mScratchSubsampleClearBytesCount[i] = data.readUnsignedShort(); 1642 mScratchSubsampleEncryptedBytesCount[i] = data.readInt(); 1643 } 1644 length -= 1645 mSubsampleEncryptionDataSize 1646 * BYTES_PER_SUBSAMPLE_ENCRYPTION_ENTRY; 1647 mEncryptionDataReadState = STATE_READING_SIGNAL_BYTE; 1648 if (length != 0) { 1649 throw new IllegalStateException(); 1650 } 1651 break; 1652 default: 1653 // Never happens. 1654 throw new IllegalStateException(); 1655 } 1656 } 1657 } else if (sampleDataPart == SAMPLE_DATA_PART_SUPPLEMENTAL 1658 && !mIncludeSupplementalData) { 1659 mSkippedSupplementalDataBytes += length; 1660 data.skipBytes(length); 1661 } else { 1662 outputSampleData(data, length); 1663 } 1664 } 1665 1666 @Override sampleMetadata( long timeUs, int flags, int size, int offset, @Nullable CryptoData cryptoData)1667 public void sampleMetadata( 1668 long timeUs, int flags, int size, int offset, @Nullable CryptoData cryptoData) { 1669 size -= mSkippedSupplementalDataBytes; 1670 mSkippedSupplementalDataBytes = 0; 1671 mOutputConsumer.onSampleCompleted( 1672 mTrackIndex, 1673 timeUs, 1674 getMediaParserFlags(flags), 1675 size - mEncryptionDataSizeToSubtractFromSampleDataSize, 1676 offset, 1677 getPopulatedCryptoInfo(cryptoData)); 1678 mEncryptionDataReadState = STATE_READING_SIGNAL_BYTE; 1679 mEncryptionDataSizeToSubtractFromSampleDataSize = 0; 1680 } 1681 1682 @Nullable getPopulatedCryptoInfo(@ullable CryptoData cryptoData)1683 private CryptoInfo getPopulatedCryptoInfo(@Nullable CryptoData cryptoData) { 1684 if (cryptoData == null) { 1685 // The sample is not encrypted. 1686 return null; 1687 } else if (mInBandCryptoInfo) { 1688 if (cryptoData != mLastReceivedCryptoData) { 1689 mLastOutputCryptoInfo = 1690 createNewCryptoInfoAndPopulateWithCryptoData(cryptoData); 1691 // We are using in-band crypto info, so the IV will be ignored. But we prevent 1692 // it from being null because toString assumes it non-null. 1693 mLastOutputCryptoInfo.iv = EMPTY_BYTE_ARRAY; 1694 } 1695 } else /* We must populate the full CryptoInfo. */ { 1696 // CryptoInfo.pattern is not accessible to the user, so the user needs to feed 1697 // this CryptoInfo directly to MediaCodec. We need to create a new CryptoInfo per 1698 // sample because of per-sample initialization vector changes. 1699 CryptoInfo newCryptoInfo = createNewCryptoInfoAndPopulateWithCryptoData(cryptoData); 1700 newCryptoInfo.iv = Arrays.copyOf(mScratchIvSpace, mScratchIvSpace.length); 1701 boolean canReuseSubsampleInfo = 1702 mLastOutputCryptoInfo != null 1703 && mLastOutputCryptoInfo.numSubSamples 1704 == mSubsampleEncryptionDataSize; 1705 for (int i = 0; i < mSubsampleEncryptionDataSize && canReuseSubsampleInfo; i++) { 1706 canReuseSubsampleInfo = 1707 mLastOutputCryptoInfo.numBytesOfClearData[i] 1708 == mScratchSubsampleClearBytesCount[i] 1709 && mLastOutputCryptoInfo.numBytesOfEncryptedData[i] 1710 == mScratchSubsampleEncryptedBytesCount[i]; 1711 } 1712 newCryptoInfo.numSubSamples = mSubsampleEncryptionDataSize; 1713 if (canReuseSubsampleInfo) { 1714 newCryptoInfo.numBytesOfClearData = mLastOutputCryptoInfo.numBytesOfClearData; 1715 newCryptoInfo.numBytesOfEncryptedData = 1716 mLastOutputCryptoInfo.numBytesOfEncryptedData; 1717 } else { 1718 newCryptoInfo.numBytesOfClearData = 1719 Arrays.copyOf( 1720 mScratchSubsampleClearBytesCount, mSubsampleEncryptionDataSize); 1721 newCryptoInfo.numBytesOfEncryptedData = 1722 Arrays.copyOf( 1723 mScratchSubsampleEncryptedBytesCount, 1724 mSubsampleEncryptionDataSize); 1725 } 1726 mLastOutputCryptoInfo = newCryptoInfo; 1727 } 1728 mLastReceivedCryptoData = cryptoData; 1729 return mLastOutputCryptoInfo; 1730 } 1731 createNewCryptoInfoAndPopulateWithCryptoData(CryptoData cryptoData)1732 private CryptoInfo createNewCryptoInfoAndPopulateWithCryptoData(CryptoData cryptoData) { 1733 CryptoInfo cryptoInfo = new CryptoInfo(); 1734 cryptoInfo.key = cryptoData.encryptionKey; 1735 cryptoInfo.mode = cryptoData.cryptoMode; 1736 if (cryptoData.clearBlocks != mLastOutputEncryptionPattern.getSkipBlocks() 1737 || cryptoData.encryptedBlocks 1738 != mLastOutputEncryptionPattern.getEncryptBlocks()) { 1739 mLastOutputEncryptionPattern = 1740 new CryptoInfo.Pattern(cryptoData.encryptedBlocks, cryptoData.clearBlocks); 1741 } 1742 cryptoInfo.setPattern(mLastOutputEncryptionPattern); 1743 return cryptoInfo; 1744 } 1745 outputSampleData(ParsableByteArray data, int length)1746 private void outputSampleData(ParsableByteArray data, int length) { 1747 mScratchParsableByteArrayAdapter.resetWithByteArray(data, length); 1748 try { 1749 // Read all bytes from data. ExoPlayer extractors expect all sample data to be 1750 // consumed by TrackOutput implementations when passing a ParsableByteArray. 1751 while (mScratchParsableByteArrayAdapter.getLength() > 0) { 1752 mOutputConsumer.onSampleDataFound( 1753 mTrackIndex, mScratchParsableByteArrayAdapter); 1754 } 1755 } catch (IOException e) { 1756 // Unexpected. 1757 throw new RuntimeException(e); 1758 } 1759 } 1760 } 1761 1762 private static final class DataReaderAdapter implements InputReader { 1763 1764 private DataReader mDataReader; 1765 private int mCurrentPosition; 1766 private long mLength; 1767 setDataReader(DataReader dataReader, long length)1768 public void setDataReader(DataReader dataReader, long length) { 1769 mDataReader = dataReader; 1770 mCurrentPosition = 0; 1771 mLength = length; 1772 } 1773 1774 // Input implementation. 1775 1776 @Override read(byte[] buffer, int offset, int readLength)1777 public int read(byte[] buffer, int offset, int readLength) throws IOException { 1778 int readBytes = 0; 1779 readBytes = mDataReader.read(buffer, offset, readLength); 1780 mCurrentPosition += readBytes; 1781 return readBytes; 1782 } 1783 1784 @Override getPosition()1785 public long getPosition() { 1786 return mCurrentPosition; 1787 } 1788 1789 @Override getLength()1790 public long getLength() { 1791 return mLength - mCurrentPosition; 1792 } 1793 } 1794 1795 private static final class ParsableByteArrayAdapter implements InputReader { 1796 1797 private ParsableByteArray mByteArray; 1798 private long mLength; 1799 private int mCurrentPosition; 1800 resetWithByteArray(ParsableByteArray byteArray, long length)1801 public void resetWithByteArray(ParsableByteArray byteArray, long length) { 1802 mByteArray = byteArray; 1803 mCurrentPosition = 0; 1804 mLength = length; 1805 } 1806 1807 // Input implementation. 1808 1809 @Override read(byte[] buffer, int offset, int readLength)1810 public int read(byte[] buffer, int offset, int readLength) { 1811 mByteArray.readBytes(buffer, offset, readLength); 1812 mCurrentPosition += readLength; 1813 return readLength; 1814 } 1815 1816 @Override getPosition()1817 public long getPosition() { 1818 return mCurrentPosition; 1819 } 1820 1821 @Override getLength()1822 public long getLength() { 1823 return mLength - mCurrentPosition; 1824 } 1825 } 1826 1827 private static final class DummyExoPlayerSeekMap 1828 implements com.google.android.exoplayer2.extractor.SeekMap { 1829 1830 @Override isSeekable()1831 public boolean isSeekable() { 1832 return true; 1833 } 1834 1835 @Override getDurationUs()1836 public long getDurationUs() { 1837 return C.TIME_UNSET; 1838 } 1839 1840 @Override getSeekPoints(long timeUs)1841 public SeekPoints getSeekPoints(long timeUs) { 1842 com.google.android.exoplayer2.extractor.SeekPoint seekPoint = 1843 new com.google.android.exoplayer2.extractor.SeekPoint( 1844 timeUs, /* position= */ 0); 1845 return new SeekPoints(seekPoint, seekPoint); 1846 } 1847 } 1848 1849 /** Creates extractor instances. */ 1850 private interface ExtractorFactory { 1851 1852 /** Returns a new extractor instance. */ createInstance()1853 Extractor createInstance(); 1854 } 1855 1856 // Private static methods. 1857 toExoPlayerCaptionFormat(MediaFormat mediaFormat)1858 private static Format toExoPlayerCaptionFormat(MediaFormat mediaFormat) { 1859 Format.Builder formatBuilder = 1860 new Format.Builder().setSampleMimeType(mediaFormat.getString(MediaFormat.KEY_MIME)); 1861 if (mediaFormat.containsKey(MediaFormat.KEY_CAPTION_SERVICE_NUMBER)) { 1862 formatBuilder.setAccessibilityChannel( 1863 mediaFormat.getInteger(MediaFormat.KEY_CAPTION_SERVICE_NUMBER)); 1864 } 1865 return formatBuilder.build(); 1866 } 1867 toMediaFormat(Format format)1868 private static MediaFormat toMediaFormat(Format format) { 1869 MediaFormat result = new MediaFormat(); 1870 setOptionalMediaFormatInt(result, MediaFormat.KEY_BIT_RATE, format.bitrate); 1871 setOptionalMediaFormatInt(result, MediaFormat.KEY_CHANNEL_COUNT, format.channelCount); 1872 1873 ColorInfo colorInfo = format.colorInfo; 1874 if (colorInfo != null) { 1875 setOptionalMediaFormatInt( 1876 result, MediaFormat.KEY_COLOR_TRANSFER, colorInfo.colorTransfer); 1877 setOptionalMediaFormatInt(result, MediaFormat.KEY_COLOR_RANGE, colorInfo.colorRange); 1878 setOptionalMediaFormatInt(result, MediaFormat.KEY_COLOR_STANDARD, colorInfo.colorSpace); 1879 1880 if (format.colorInfo.hdrStaticInfo != null) { 1881 result.setByteBuffer( 1882 MediaFormat.KEY_HDR_STATIC_INFO, 1883 ByteBuffer.wrap(format.colorInfo.hdrStaticInfo)); 1884 } 1885 } 1886 1887 setOptionalMediaFormatString(result, MediaFormat.KEY_MIME, format.sampleMimeType); 1888 setOptionalMediaFormatString(result, MediaFormat.KEY_CODECS_STRING, format.codecs); 1889 if (format.frameRate != Format.NO_VALUE) { 1890 result.setFloat(MediaFormat.KEY_FRAME_RATE, format.frameRate); 1891 } 1892 setOptionalMediaFormatInt(result, MediaFormat.KEY_WIDTH, format.width); 1893 setOptionalMediaFormatInt(result, MediaFormat.KEY_HEIGHT, format.height); 1894 1895 List<byte[]> initData = format.initializationData; 1896 for (int i = 0; i < initData.size(); i++) { 1897 result.setByteBuffer("csd-" + i, ByteBuffer.wrap(initData.get(i))); 1898 } 1899 setPcmEncoding(format, result); 1900 setOptionalMediaFormatString(result, MediaFormat.KEY_LANGUAGE, format.language); 1901 setOptionalMediaFormatInt(result, MediaFormat.KEY_MAX_INPUT_SIZE, format.maxInputSize); 1902 setOptionalMediaFormatInt(result, MediaFormat.KEY_ROTATION, format.rotationDegrees); 1903 setOptionalMediaFormatInt(result, MediaFormat.KEY_SAMPLE_RATE, format.sampleRate); 1904 setOptionalMediaFormatInt( 1905 result, MediaFormat.KEY_CAPTION_SERVICE_NUMBER, format.accessibilityChannel); 1906 1907 int selectionFlags = format.selectionFlags; 1908 result.setInteger( 1909 MediaFormat.KEY_IS_AUTOSELECT, selectionFlags & C.SELECTION_FLAG_AUTOSELECT); 1910 result.setInteger(MediaFormat.KEY_IS_DEFAULT, selectionFlags & C.SELECTION_FLAG_DEFAULT); 1911 result.setInteger( 1912 MediaFormat.KEY_IS_FORCED_SUBTITLE, selectionFlags & C.SELECTION_FLAG_FORCED); 1913 1914 setOptionalMediaFormatInt(result, MediaFormat.KEY_ENCODER_DELAY, format.encoderDelay); 1915 setOptionalMediaFormatInt(result, MediaFormat.KEY_ENCODER_PADDING, format.encoderPadding); 1916 1917 if (format.pixelWidthHeightRatio != Format.NO_VALUE && format.pixelWidthHeightRatio != 0) { 1918 int parWidth = 1; 1919 int parHeight = 1; 1920 if (format.pixelWidthHeightRatio < 1.0f) { 1921 parHeight = 1 << 30; 1922 parWidth = (int) (format.pixelWidthHeightRatio * parHeight); 1923 } else if (format.pixelWidthHeightRatio > 1.0f) { 1924 parWidth = 1 << 30; 1925 parHeight = (int) (parWidth / format.pixelWidthHeightRatio); 1926 } 1927 result.setInteger(MediaFormat.KEY_PIXEL_ASPECT_RATIO_WIDTH, parWidth); 1928 result.setInteger(MediaFormat.KEY_PIXEL_ASPECT_RATIO_HEIGHT, parHeight); 1929 result.setFloat("pixel-width-height-ratio-float", format.pixelWidthHeightRatio); 1930 } 1931 if (format.drmInitData != null) { 1932 // The crypto mode is propagated along with sample metadata. We also include it in the 1933 // format for convenient use from ExoPlayer. 1934 result.setString("crypto-mode-fourcc", format.drmInitData.schemeType); 1935 } 1936 if (format.subsampleOffsetUs != Format.OFFSET_SAMPLE_RELATIVE) { 1937 result.setLong("subsample-offset-us-long", format.subsampleOffsetUs); 1938 } 1939 // LACK OF SUPPORT FOR: 1940 // format.id; 1941 // format.metadata; 1942 // format.stereoMode; 1943 return result; 1944 } 1945 toByteBuffer(long[] longArray)1946 private static ByteBuffer toByteBuffer(long[] longArray) { 1947 ByteBuffer byteBuffer = ByteBuffer.allocateDirect(longArray.length * Long.BYTES); 1948 for (long element : longArray) { 1949 byteBuffer.putLong(element); 1950 } 1951 byteBuffer.flip(); 1952 return byteBuffer; 1953 } 1954 toByteBuffer(int[] intArray)1955 private static ByteBuffer toByteBuffer(int[] intArray) { 1956 ByteBuffer byteBuffer = ByteBuffer.allocateDirect(intArray.length * Integer.BYTES); 1957 for (int element : intArray) { 1958 byteBuffer.putInt(element); 1959 } 1960 byteBuffer.flip(); 1961 return byteBuffer; 1962 } 1963 toTypeString(int type)1964 private static String toTypeString(int type) { 1965 switch (type) { 1966 case C.TRACK_TYPE_VIDEO: 1967 return "video"; 1968 case C.TRACK_TYPE_AUDIO: 1969 return "audio"; 1970 case C.TRACK_TYPE_TEXT: 1971 return "text"; 1972 case C.TRACK_TYPE_METADATA: 1973 return "metadata"; 1974 default: 1975 return "unknown"; 1976 } 1977 } 1978 setPcmEncoding(Format format, MediaFormat result)1979 private static void setPcmEncoding(Format format, MediaFormat result) { 1980 int exoPcmEncoding = format.pcmEncoding; 1981 setOptionalMediaFormatInt(result, "exo-pcm-encoding", format.pcmEncoding); 1982 int mediaFormatPcmEncoding; 1983 switch (exoPcmEncoding) { 1984 case C.ENCODING_PCM_8BIT: 1985 mediaFormatPcmEncoding = AudioFormat.ENCODING_PCM_8BIT; 1986 break; 1987 case C.ENCODING_PCM_16BIT: 1988 mediaFormatPcmEncoding = AudioFormat.ENCODING_PCM_16BIT; 1989 break; 1990 case C.ENCODING_PCM_FLOAT: 1991 mediaFormatPcmEncoding = AudioFormat.ENCODING_PCM_FLOAT; 1992 break; 1993 default: 1994 // No matching value. Do nothing. 1995 return; 1996 } 1997 result.setInteger(MediaFormat.KEY_PCM_ENCODING, mediaFormatPcmEncoding); 1998 } 1999 setOptionalMediaFormatInt(MediaFormat mediaFormat, String key, int value)2000 private static void setOptionalMediaFormatInt(MediaFormat mediaFormat, String key, int value) { 2001 if (value != Format.NO_VALUE) { 2002 mediaFormat.setInteger(key, value); 2003 } 2004 } 2005 setOptionalMediaFormatString( MediaFormat mediaFormat, String key, @Nullable String value)2006 private static void setOptionalMediaFormatString( 2007 MediaFormat mediaFormat, String key, @Nullable String value) { 2008 if (value != null) { 2009 mediaFormat.setString(key, value); 2010 } 2011 } 2012 toFrameworkDrmInitData( com.google.android.exoplayer2.drm.DrmInitData exoDrmInitData)2013 private DrmInitData toFrameworkDrmInitData( 2014 com.google.android.exoplayer2.drm.DrmInitData exoDrmInitData) { 2015 try { 2016 return exoDrmInitData != null && mSchemeInitDataConstructor != null 2017 ? new MediaParserDrmInitData(exoDrmInitData) 2018 : null; 2019 } catch (Throwable e) { 2020 if (!mLoggedSchemeInitDataCreationException) { 2021 mLoggedSchemeInitDataCreationException = true; 2022 Log.e(TAG, "Unable to create SchemeInitData instance."); 2023 } 2024 return null; 2025 } 2026 } 2027 2028 /** Returns a new {@link SeekPoint} equivalent to the given {@code exoPlayerSeekPoint}. */ toSeekPoint( com.google.android.exoplayer2.extractor.SeekPoint exoPlayerSeekPoint)2029 private static SeekPoint toSeekPoint( 2030 com.google.android.exoplayer2.extractor.SeekPoint exoPlayerSeekPoint) { 2031 return new SeekPoint(exoPlayerSeekPoint.timeUs, exoPlayerSeekPoint.position); 2032 } 2033 assertValidNames(@onNull String[] names)2034 private static void assertValidNames(@NonNull String[] names) { 2035 for (String name : names) { 2036 if (!EXTRACTOR_FACTORIES_BY_NAME.containsKey(name)) { 2037 throw new IllegalArgumentException( 2038 "Invalid extractor name: " 2039 + name 2040 + ". Supported parsers are: " 2041 + TextUtils.join(", ", EXTRACTOR_FACTORIES_BY_NAME.keySet()) 2042 + "."); 2043 } 2044 } 2045 } 2046 getMediaParserFlags(int flags)2047 private int getMediaParserFlags(int flags) { 2048 @SampleFlags int result = 0; 2049 result |= (flags & C.BUFFER_FLAG_ENCRYPTED) != 0 ? SAMPLE_FLAG_ENCRYPTED : 0; 2050 result |= (flags & C.BUFFER_FLAG_KEY_FRAME) != 0 ? SAMPLE_FLAG_KEY_FRAME : 0; 2051 result |= (flags & C.BUFFER_FLAG_DECODE_ONLY) != 0 ? SAMPLE_FLAG_DECODE_ONLY : 0; 2052 result |= 2053 (flags & C.BUFFER_FLAG_HAS_SUPPLEMENTAL_DATA) != 0 && mIncludeSupplementalData 2054 ? SAMPLE_FLAG_HAS_SUPPLEMENTAL_DATA 2055 : 0; 2056 result |= (flags & C.BUFFER_FLAG_LAST_SAMPLE) != 0 ? SAMPLE_FLAG_LAST_SAMPLE : 0; 2057 return result; 2058 } 2059 2060 @Nullable getSchemeInitDataConstructor()2061 private static Constructor<DrmInitData.SchemeInitData> getSchemeInitDataConstructor() { 2062 // TODO: Use constructor statically when available. 2063 Constructor<DrmInitData.SchemeInitData> constructor; 2064 try { 2065 return DrmInitData.SchemeInitData.class.getConstructor( 2066 UUID.class, String.class, byte[].class); 2067 } catch (Throwable e) { 2068 Log.e(TAG, "Unable to get SchemeInitData constructor."); 2069 return null; 2070 } 2071 } 2072 2073 // Static initialization. 2074 2075 static { 2076 // Using a LinkedHashMap to keep the insertion order when iterating over the keys. 2077 LinkedHashMap<String, ExtractorFactory> extractorFactoriesByName = new LinkedHashMap<>(); 2078 // Parsers are ordered to match ExoPlayer's DefaultExtractorsFactory extractor ordering, 2079 // which in turn aims to minimize the chances of incorrect extractor selections. extractorFactoriesByName.put(PARSER_NAME_MATROSKA, MatroskaExtractor::new)2080 extractorFactoriesByName.put(PARSER_NAME_MATROSKA, MatroskaExtractor::new); extractorFactoriesByName.put(PARSER_NAME_FMP4, FragmentedMp4Extractor::new)2081 extractorFactoriesByName.put(PARSER_NAME_FMP4, FragmentedMp4Extractor::new); extractorFactoriesByName.put(PARSER_NAME_MP4, Mp4Extractor::new)2082 extractorFactoriesByName.put(PARSER_NAME_MP4, Mp4Extractor::new); extractorFactoriesByName.put(PARSER_NAME_MP3, Mp3Extractor::new)2083 extractorFactoriesByName.put(PARSER_NAME_MP3, Mp3Extractor::new); extractorFactoriesByName.put(PARSER_NAME_ADTS, AdtsExtractor::new)2084 extractorFactoriesByName.put(PARSER_NAME_ADTS, AdtsExtractor::new); extractorFactoriesByName.put(PARSER_NAME_AC3, Ac3Extractor::new)2085 extractorFactoriesByName.put(PARSER_NAME_AC3, Ac3Extractor::new); extractorFactoriesByName.put(PARSER_NAME_TS, TsExtractor::new)2086 extractorFactoriesByName.put(PARSER_NAME_TS, TsExtractor::new); extractorFactoriesByName.put(PARSER_NAME_FLV, FlvExtractor::new)2087 extractorFactoriesByName.put(PARSER_NAME_FLV, FlvExtractor::new); extractorFactoriesByName.put(PARSER_NAME_OGG, OggExtractor::new)2088 extractorFactoriesByName.put(PARSER_NAME_OGG, OggExtractor::new); extractorFactoriesByName.put(PARSER_NAME_PS, PsExtractor::new)2089 extractorFactoriesByName.put(PARSER_NAME_PS, PsExtractor::new); extractorFactoriesByName.put(PARSER_NAME_WAV, WavExtractor::new)2090 extractorFactoriesByName.put(PARSER_NAME_WAV, WavExtractor::new); extractorFactoriesByName.put(PARSER_NAME_AMR, AmrExtractor::new)2091 extractorFactoriesByName.put(PARSER_NAME_AMR, AmrExtractor::new); extractorFactoriesByName.put(PARSER_NAME_AC4, Ac4Extractor::new)2092 extractorFactoriesByName.put(PARSER_NAME_AC4, Ac4Extractor::new); extractorFactoriesByName.put(PARSER_NAME_FLAC, FlacExtractor::new)2093 extractorFactoriesByName.put(PARSER_NAME_FLAC, FlacExtractor::new); 2094 EXTRACTOR_FACTORIES_BY_NAME = Collections.unmodifiableMap(extractorFactoriesByName); 2095 2096 HashMap<String, Class> expectedTypeByParameterName = new HashMap<>(); expectedTypeByParameterName.put(PARAMETER_ADTS_ENABLE_CBR_SEEKING, Boolean.class)2097 expectedTypeByParameterName.put(PARAMETER_ADTS_ENABLE_CBR_SEEKING, Boolean.class); expectedTypeByParameterName.put(PARAMETER_AMR_ENABLE_CBR_SEEKING, Boolean.class)2098 expectedTypeByParameterName.put(PARAMETER_AMR_ENABLE_CBR_SEEKING, Boolean.class); expectedTypeByParameterName.put(PARAMETER_FLAC_DISABLE_ID3, Boolean.class)2099 expectedTypeByParameterName.put(PARAMETER_FLAC_DISABLE_ID3, Boolean.class); expectedTypeByParameterName.put(PARAMETER_MP4_IGNORE_EDIT_LISTS, Boolean.class)2100 expectedTypeByParameterName.put(PARAMETER_MP4_IGNORE_EDIT_LISTS, Boolean.class); expectedTypeByParameterName.put(PARAMETER_MP4_IGNORE_TFDT_BOX, Boolean.class)2101 expectedTypeByParameterName.put(PARAMETER_MP4_IGNORE_TFDT_BOX, Boolean.class); expectedTypeByParameterName.put( PARAMETER_MP4_TREAT_VIDEO_FRAMES_AS_KEYFRAMES, Boolean.class)2102 expectedTypeByParameterName.put( 2103 PARAMETER_MP4_TREAT_VIDEO_FRAMES_AS_KEYFRAMES, Boolean.class); expectedTypeByParameterName.put(PARAMETER_MATROSKA_DISABLE_CUES_SEEKING, Boolean.class)2104 expectedTypeByParameterName.put(PARAMETER_MATROSKA_DISABLE_CUES_SEEKING, Boolean.class); expectedTypeByParameterName.put(PARAMETER_MP3_DISABLE_ID3, Boolean.class)2105 expectedTypeByParameterName.put(PARAMETER_MP3_DISABLE_ID3, Boolean.class); expectedTypeByParameterName.put(PARAMETER_MP3_ENABLE_CBR_SEEKING, Boolean.class)2106 expectedTypeByParameterName.put(PARAMETER_MP3_ENABLE_CBR_SEEKING, Boolean.class); expectedTypeByParameterName.put(PARAMETER_MP3_ENABLE_INDEX_SEEKING, Boolean.class)2107 expectedTypeByParameterName.put(PARAMETER_MP3_ENABLE_INDEX_SEEKING, Boolean.class); expectedTypeByParameterName.put(PARAMETER_TS_MODE, String.class)2108 expectedTypeByParameterName.put(PARAMETER_TS_MODE, String.class); expectedTypeByParameterName.put(PARAMETER_TS_ALLOW_NON_IDR_AVC_KEYFRAMES, Boolean.class)2109 expectedTypeByParameterName.put(PARAMETER_TS_ALLOW_NON_IDR_AVC_KEYFRAMES, Boolean.class); expectedTypeByParameterName.put(PARAMETER_TS_IGNORE_AAC_STREAM, Boolean.class)2110 expectedTypeByParameterName.put(PARAMETER_TS_IGNORE_AAC_STREAM, Boolean.class); expectedTypeByParameterName.put(PARAMETER_TS_IGNORE_AVC_STREAM, Boolean.class)2111 expectedTypeByParameterName.put(PARAMETER_TS_IGNORE_AVC_STREAM, Boolean.class); expectedTypeByParameterName.put(PARAMETER_TS_IGNORE_SPLICE_INFO_STREAM, Boolean.class)2112 expectedTypeByParameterName.put(PARAMETER_TS_IGNORE_SPLICE_INFO_STREAM, Boolean.class); expectedTypeByParameterName.put(PARAMETER_TS_DETECT_ACCESS_UNITS, Boolean.class)2113 expectedTypeByParameterName.put(PARAMETER_TS_DETECT_ACCESS_UNITS, Boolean.class); expectedTypeByParameterName.put(PARAMETER_TS_ENABLE_HDMV_DTS_AUDIO_STREAMS, Boolean.class)2114 expectedTypeByParameterName.put(PARAMETER_TS_ENABLE_HDMV_DTS_AUDIO_STREAMS, Boolean.class); expectedTypeByParameterName.put(PARAMETER_IN_BAND_CRYPTO_INFO, Boolean.class)2115 expectedTypeByParameterName.put(PARAMETER_IN_BAND_CRYPTO_INFO, Boolean.class); expectedTypeByParameterName.put(PARAMETER_INCLUDE_SUPPLEMENTAL_DATA, Boolean.class)2116 expectedTypeByParameterName.put(PARAMETER_INCLUDE_SUPPLEMENTAL_DATA, Boolean.class); expectedTypeByParameterName.put(PARAMETER_IGNORE_TIMESTAMP_OFFSET, Boolean.class)2117 expectedTypeByParameterName.put(PARAMETER_IGNORE_TIMESTAMP_OFFSET, Boolean.class); expectedTypeByParameterName.put(PARAMETER_EAGERLY_EXPOSE_TRACKTYPE, Boolean.class)2118 expectedTypeByParameterName.put(PARAMETER_EAGERLY_EXPOSE_TRACKTYPE, Boolean.class); expectedTypeByParameterName.put(PARAMETER_EXPOSE_DUMMY_SEEKMAP, Boolean.class)2119 expectedTypeByParameterName.put(PARAMETER_EXPOSE_DUMMY_SEEKMAP, Boolean.class); expectedTypeByParameterName.put( PARAMETER_EXPOSE_CHUNK_INDEX_AS_MEDIA_FORMAT, Boolean.class)2120 expectedTypeByParameterName.put( 2121 PARAMETER_EXPOSE_CHUNK_INDEX_AS_MEDIA_FORMAT, Boolean.class); expectedTypeByParameterName.put( PARAMETER_OVERRIDE_IN_BAND_CAPTION_DECLARATIONS, Boolean.class)2122 expectedTypeByParameterName.put( 2123 PARAMETER_OVERRIDE_IN_BAND_CAPTION_DECLARATIONS, Boolean.class); expectedTypeByParameterName.put(PARAMETER_EXPOSE_EMSG_TRACK, Boolean.class)2124 expectedTypeByParameterName.put(PARAMETER_EXPOSE_EMSG_TRACK, Boolean.class); 2125 // We do not check PARAMETER_EXPOSE_CAPTION_FORMATS here, and we do it in setParameters 2126 // instead. Checking that the value is a List is insufficient to catch wrong parameter 2127 // value types. 2128 EXPECTED_TYPE_BY_PARAMETER_NAME = Collections.unmodifiableMap(expectedTypeByParameterName); 2129 } 2130 } 2131