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