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