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