1 /*
2  * Copyright (C) 2016 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 com.google.android.exoplayer2.extractor.mkv;
17 
18 import android.util.Pair;
19 import android.util.SparseArray;
20 import androidx.annotation.CallSuper;
21 import androidx.annotation.IntDef;
22 import androidx.annotation.Nullable;
23 import com.google.android.exoplayer2.C;
24 import com.google.android.exoplayer2.Format;
25 import com.google.android.exoplayer2.ParserException;
26 import com.google.android.exoplayer2.audio.Ac3Util;
27 import com.google.android.exoplayer2.audio.MpegAudioUtil;
28 import com.google.android.exoplayer2.drm.DrmInitData;
29 import com.google.android.exoplayer2.drm.DrmInitData.SchemeData;
30 import com.google.android.exoplayer2.extractor.ChunkIndex;
31 import com.google.android.exoplayer2.extractor.Extractor;
32 import com.google.android.exoplayer2.extractor.ExtractorInput;
33 import com.google.android.exoplayer2.extractor.ExtractorOutput;
34 import com.google.android.exoplayer2.extractor.ExtractorsFactory;
35 import com.google.android.exoplayer2.extractor.PositionHolder;
36 import com.google.android.exoplayer2.extractor.SeekMap;
37 import com.google.android.exoplayer2.extractor.TrackOutput;
38 import com.google.android.exoplayer2.util.Assertions;
39 import com.google.android.exoplayer2.util.Log;
40 import com.google.android.exoplayer2.util.LongArray;
41 import com.google.android.exoplayer2.util.MimeTypes;
42 import com.google.android.exoplayer2.util.NalUnitUtil;
43 import com.google.android.exoplayer2.util.ParsableByteArray;
44 import com.google.android.exoplayer2.util.Util;
45 import com.google.android.exoplayer2.video.AvcConfig;
46 import com.google.android.exoplayer2.video.ColorInfo;
47 import com.google.android.exoplayer2.video.HevcConfig;
48 import java.io.IOException;
49 import java.lang.annotation.Documented;
50 import java.lang.annotation.Retention;
51 import java.lang.annotation.RetentionPolicy;
52 import java.nio.ByteBuffer;
53 import java.nio.ByteOrder;
54 import java.util.ArrayList;
55 import java.util.Arrays;
56 import java.util.Collections;
57 import java.util.HashMap;
58 import java.util.List;
59 import java.util.Locale;
60 import java.util.Map;
61 import java.util.UUID;
62 import org.checkerframework.checker.nullness.compatqual.NullableType;
63 import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
64 
65 /** Extracts data from the Matroska and WebM container formats. */
66 public class MatroskaExtractor implements Extractor {
67 
68   /** Factory for {@link MatroskaExtractor} instances. */
69   public static final ExtractorsFactory FACTORY = () -> new Extractor[] {new MatroskaExtractor()};
70 
71   /**
72    * Flags controlling the behavior of the extractor. Possible flag value is {@link
73    * #FLAG_DISABLE_SEEK_FOR_CUES}.
74    */
75   @Documented
76   @Retention(RetentionPolicy.SOURCE)
77   @IntDef(
78       flag = true,
79       value = {FLAG_DISABLE_SEEK_FOR_CUES})
80   public @interface Flags {}
81   /**
82    * Flag to disable seeking for cues.
83    * <p>
84    * Normally (i.e. when this flag is not set) the extractor will seek to the cues element if its
85    * position is specified in the seek head and if it's after the first cluster. Setting this flag
86    * disables seeking to the cues element. If the cues element is after the first cluster then the
87    * media is treated as being unseekable.
88    */
89   public static final int FLAG_DISABLE_SEEK_FOR_CUES = 1;
90 
91   private static final String TAG = "MatroskaExtractor";
92 
93   private static final int UNSET_ENTRY_ID = -1;
94 
95   private static final int BLOCK_STATE_START = 0;
96   private static final int BLOCK_STATE_HEADER = 1;
97   private static final int BLOCK_STATE_DATA = 2;
98 
99   private static final String DOC_TYPE_MATROSKA = "matroska";
100   private static final String DOC_TYPE_WEBM = "webm";
101   private static final String CODEC_ID_VP8 = "V_VP8";
102   private static final String CODEC_ID_VP9 = "V_VP9";
103   private static final String CODEC_ID_AV1 = "V_AV1";
104   private static final String CODEC_ID_MPEG2 = "V_MPEG2";
105   private static final String CODEC_ID_MPEG4_SP = "V_MPEG4/ISO/SP";
106   private static final String CODEC_ID_MPEG4_ASP = "V_MPEG4/ISO/ASP";
107   private static final String CODEC_ID_MPEG4_AP = "V_MPEG4/ISO/AP";
108   private static final String CODEC_ID_H264 = "V_MPEG4/ISO/AVC";
109   private static final String CODEC_ID_H265 = "V_MPEGH/ISO/HEVC";
110   private static final String CODEC_ID_FOURCC = "V_MS/VFW/FOURCC";
111   private static final String CODEC_ID_THEORA = "V_THEORA";
112   private static final String CODEC_ID_VORBIS = "A_VORBIS";
113   private static final String CODEC_ID_OPUS = "A_OPUS";
114   private static final String CODEC_ID_AAC = "A_AAC";
115   private static final String CODEC_ID_MP2 = "A_MPEG/L2";
116   private static final String CODEC_ID_MP3 = "A_MPEG/L3";
117   private static final String CODEC_ID_AC3 = "A_AC3";
118   private static final String CODEC_ID_E_AC3 = "A_EAC3";
119   private static final String CODEC_ID_TRUEHD = "A_TRUEHD";
120   private static final String CODEC_ID_DTS = "A_DTS";
121   private static final String CODEC_ID_DTS_EXPRESS = "A_DTS/EXPRESS";
122   private static final String CODEC_ID_DTS_LOSSLESS = "A_DTS/LOSSLESS";
123   private static final String CODEC_ID_FLAC = "A_FLAC";
124   private static final String CODEC_ID_ACM = "A_MS/ACM";
125   private static final String CODEC_ID_PCM_INT_LIT = "A_PCM/INT/LIT";
126   private static final String CODEC_ID_SUBRIP = "S_TEXT/UTF8";
127   private static final String CODEC_ID_ASS = "S_TEXT/ASS";
128   private static final String CODEC_ID_VOBSUB = "S_VOBSUB";
129   private static final String CODEC_ID_PGS = "S_HDMV/PGS";
130   private static final String CODEC_ID_DVBSUB = "S_DVBSUB";
131 
132   private static final int VORBIS_MAX_INPUT_SIZE = 8192;
133   private static final int OPUS_MAX_INPUT_SIZE = 5760;
134   private static final int ENCRYPTION_IV_SIZE = 8;
135   private static final int TRACK_TYPE_AUDIO = 2;
136 
137   private static final int ID_EBML = 0x1A45DFA3;
138   private static final int ID_EBML_READ_VERSION = 0x42F7;
139   private static final int ID_DOC_TYPE = 0x4282;
140   private static final int ID_DOC_TYPE_READ_VERSION = 0x4285;
141   private static final int ID_SEGMENT = 0x18538067;
142   private static final int ID_SEGMENT_INFO = 0x1549A966;
143   private static final int ID_SEEK_HEAD = 0x114D9B74;
144   private static final int ID_SEEK = 0x4DBB;
145   private static final int ID_SEEK_ID = 0x53AB;
146   private static final int ID_SEEK_POSITION = 0x53AC;
147   private static final int ID_INFO = 0x1549A966;
148   private static final int ID_TIMECODE_SCALE = 0x2AD7B1;
149   private static final int ID_DURATION = 0x4489;
150   private static final int ID_CLUSTER = 0x1F43B675;
151   private static final int ID_TIME_CODE = 0xE7;
152   private static final int ID_SIMPLE_BLOCK = 0xA3;
153   private static final int ID_BLOCK_GROUP = 0xA0;
154   private static final int ID_BLOCK = 0xA1;
155   private static final int ID_BLOCK_DURATION = 0x9B;
156   private static final int ID_BLOCK_ADDITIONS = 0x75A1;
157   private static final int ID_BLOCK_MORE = 0xA6;
158   private static final int ID_BLOCK_ADD_ID = 0xEE;
159   private static final int ID_BLOCK_ADDITIONAL = 0xA5;
160   private static final int ID_REFERENCE_BLOCK = 0xFB;
161   private static final int ID_TRACKS = 0x1654AE6B;
162   private static final int ID_TRACK_ENTRY = 0xAE;
163   private static final int ID_TRACK_NUMBER = 0xD7;
164   private static final int ID_TRACK_TYPE = 0x83;
165   private static final int ID_FLAG_DEFAULT = 0x88;
166   private static final int ID_FLAG_FORCED = 0x55AA;
167   private static final int ID_DEFAULT_DURATION = 0x23E383;
168   private static final int ID_MAX_BLOCK_ADDITION_ID = 0x55EE;
169   private static final int ID_NAME = 0x536E;
170   private static final int ID_CODEC_ID = 0x86;
171   private static final int ID_CODEC_PRIVATE = 0x63A2;
172   private static final int ID_CODEC_DELAY = 0x56AA;
173   private static final int ID_SEEK_PRE_ROLL = 0x56BB;
174   private static final int ID_VIDEO = 0xE0;
175   private static final int ID_PIXEL_WIDTH = 0xB0;
176   private static final int ID_PIXEL_HEIGHT = 0xBA;
177   private static final int ID_DISPLAY_WIDTH = 0x54B0;
178   private static final int ID_DISPLAY_HEIGHT = 0x54BA;
179   private static final int ID_DISPLAY_UNIT = 0x54B2;
180   private static final int ID_AUDIO = 0xE1;
181   private static final int ID_CHANNELS = 0x9F;
182   private static final int ID_AUDIO_BIT_DEPTH = 0x6264;
183   private static final int ID_SAMPLING_FREQUENCY = 0xB5;
184   private static final int ID_CONTENT_ENCODINGS = 0x6D80;
185   private static final int ID_CONTENT_ENCODING = 0x6240;
186   private static final int ID_CONTENT_ENCODING_ORDER = 0x5031;
187   private static final int ID_CONTENT_ENCODING_SCOPE = 0x5032;
188   private static final int ID_CONTENT_COMPRESSION = 0x5034;
189   private static final int ID_CONTENT_COMPRESSION_ALGORITHM = 0x4254;
190   private static final int ID_CONTENT_COMPRESSION_SETTINGS = 0x4255;
191   private static final int ID_CONTENT_ENCRYPTION = 0x5035;
192   private static final int ID_CONTENT_ENCRYPTION_ALGORITHM = 0x47E1;
193   private static final int ID_CONTENT_ENCRYPTION_KEY_ID = 0x47E2;
194   private static final int ID_CONTENT_ENCRYPTION_AES_SETTINGS = 0x47E7;
195   private static final int ID_CONTENT_ENCRYPTION_AES_SETTINGS_CIPHER_MODE = 0x47E8;
196   private static final int ID_CUES = 0x1C53BB6B;
197   private static final int ID_CUE_POINT = 0xBB;
198   private static final int ID_CUE_TIME = 0xB3;
199   private static final int ID_CUE_TRACK_POSITIONS = 0xB7;
200   private static final int ID_CUE_CLUSTER_POSITION = 0xF1;
201   private static final int ID_LANGUAGE = 0x22B59C;
202   private static final int ID_PROJECTION = 0x7670;
203   private static final int ID_PROJECTION_TYPE = 0x7671;
204   private static final int ID_PROJECTION_PRIVATE = 0x7672;
205   private static final int ID_PROJECTION_POSE_YAW = 0x7673;
206   private static final int ID_PROJECTION_POSE_PITCH = 0x7674;
207   private static final int ID_PROJECTION_POSE_ROLL = 0x7675;
208   private static final int ID_STEREO_MODE = 0x53B8;
209   private static final int ID_COLOUR = 0x55B0;
210   private static final int ID_COLOUR_RANGE = 0x55B9;
211   private static final int ID_COLOUR_TRANSFER = 0x55BA;
212   private static final int ID_COLOUR_PRIMARIES = 0x55BB;
213   private static final int ID_MAX_CLL = 0x55BC;
214   private static final int ID_MAX_FALL = 0x55BD;
215   private static final int ID_MASTERING_METADATA = 0x55D0;
216   private static final int ID_PRIMARY_R_CHROMATICITY_X = 0x55D1;
217   private static final int ID_PRIMARY_R_CHROMATICITY_Y = 0x55D2;
218   private static final int ID_PRIMARY_G_CHROMATICITY_X = 0x55D3;
219   private static final int ID_PRIMARY_G_CHROMATICITY_Y = 0x55D4;
220   private static final int ID_PRIMARY_B_CHROMATICITY_X = 0x55D5;
221   private static final int ID_PRIMARY_B_CHROMATICITY_Y = 0x55D6;
222   private static final int ID_WHITE_POINT_CHROMATICITY_X = 0x55D7;
223   private static final int ID_WHITE_POINT_CHROMATICITY_Y = 0x55D8;
224   private static final int ID_LUMNINANCE_MAX = 0x55D9;
225   private static final int ID_LUMNINANCE_MIN = 0x55DA;
226 
227   /**
228    * BlockAddID value for ITU T.35 metadata in a VP9 track. See also
229    * https://www.webmproject.org/docs/container/.
230    */
231   private static final int BLOCK_ADDITIONAL_ID_VP9_ITU_T_35 = 4;
232 
233   private static final int LACING_NONE = 0;
234   private static final int LACING_XIPH = 1;
235   private static final int LACING_FIXED_SIZE = 2;
236   private static final int LACING_EBML = 3;
237 
238   private static final int FOURCC_COMPRESSION_DIVX = 0x58564944;
239   private static final int FOURCC_COMPRESSION_H263 = 0x33363248;
240   private static final int FOURCC_COMPRESSION_VC1 = 0x31435657;
241 
242   /**
243    * A template for the prefix that must be added to each subrip sample.
244    *
245    * <p>The display time of each subtitle is passed as {@code timeUs} to {@link
246    * TrackOutput#sampleMetadata}. The start and end timecodes in this template are relative to
247    * {@code timeUs}. Hence the start timecode is always zero. The 12 byte end timecode starting at
248    * {@link #SUBRIP_PREFIX_END_TIMECODE_OFFSET} is set to a dummy value, and must be replaced with
249    * the duration of the subtitle.
250    *
251    * <p>Equivalent to the UTF-8 string: "1\n00:00:00,000 --> 00:00:00,000\n".
252    */
253   private static final byte[] SUBRIP_PREFIX =
254       new byte[] {
255         49, 10, 48, 48, 58, 48, 48, 58, 48, 48, 44, 48, 48, 48, 32, 45, 45, 62, 32, 48, 48, 58, 48,
256         48, 58, 48, 48, 44, 48, 48, 48, 10
257       };
258   /**
259    * The byte offset of the end timecode in {@link #SUBRIP_PREFIX}.
260    */
261   private static final int SUBRIP_PREFIX_END_TIMECODE_OFFSET = 19;
262   /**
263    * The value by which to divide a time in microseconds to convert it to the unit of the last value
264    * in a subrip timecode (milliseconds).
265    */
266   private static final long SUBRIP_TIMECODE_LAST_VALUE_SCALING_FACTOR = 1000;
267   /**
268    * The format of a subrip timecode.
269    */
270   private static final String SUBRIP_TIMECODE_FORMAT = "%02d:%02d:%02d,%03d";
271 
272   /**
273    * Matroska specific format line for SSA subtitles.
274    */
275   private static final byte[] SSA_DIALOGUE_FORMAT = Util.getUtf8Bytes("Format: Start, End, "
276       + "ReadOrder, Layer, Style, Name, MarginL, MarginR, MarginV, Effect, Text");
277   /**
278    * A template for the prefix that must be added to each SSA sample.
279    *
280    * <p>The display time of each subtitle is passed as {@code timeUs} to {@link
281    * TrackOutput#sampleMetadata}. The start and end timecodes in this template are relative to
282    * {@code timeUs}. Hence the start timecode is always zero. The 12 byte end timecode starting at
283    * {@link #SUBRIP_PREFIX_END_TIMECODE_OFFSET} is set to a dummy value, and must be replaced with
284    * the duration of the subtitle.
285    *
286    * <p>Equivalent to the UTF-8 string: "Dialogue: 0:00:00:00,0:00:00:00,".
287    */
288   private static final byte[] SSA_PREFIX =
289       new byte[] {
290         68, 105, 97, 108, 111, 103, 117, 101, 58, 32, 48, 58, 48, 48, 58, 48, 48, 58, 48, 48, 44,
291         48, 58, 48, 48, 58, 48, 48, 58, 48, 48, 44
292       };
293   /**
294    * The byte offset of the end timecode in {@link #SSA_PREFIX}.
295    */
296   private static final int SSA_PREFIX_END_TIMECODE_OFFSET = 21;
297   /**
298    * The value by which to divide a time in microseconds to convert it to the unit of the last value
299    * in an SSA timecode (1/100ths of a second).
300    */
301   private static final long SSA_TIMECODE_LAST_VALUE_SCALING_FACTOR = 10000;
302   /**
303    * The format of an SSA timecode.
304    */
305   private static final String SSA_TIMECODE_FORMAT = "%01d:%02d:%02d:%02d";
306 
307   /**
308    * The length in bytes of a WAVEFORMATEX structure.
309    */
310   private static final int WAVE_FORMAT_SIZE = 18;
311   /**
312    * Format tag indicating a WAVEFORMATEXTENSIBLE structure.
313    */
314   private static final int WAVE_FORMAT_EXTENSIBLE = 0xFFFE;
315   /**
316    * Format tag for PCM.
317    */
318   private static final int WAVE_FORMAT_PCM = 1;
319   /**
320    * Sub format for PCM.
321    */
322   private static final UUID WAVE_SUBFORMAT_PCM = new UUID(0x0100000000001000L, 0x800000AA00389B71L);
323 
324   /** Some HTC devices signal rotation in track names. */
325   private static final Map<String, Integer> TRACK_NAME_TO_ROTATION_DEGREES;
326 
327   static {
328     Map<String, Integer> trackNameToRotationDegrees = new HashMap<>();
329     trackNameToRotationDegrees.put("htc_video_rotA-000", 0);
330     trackNameToRotationDegrees.put("htc_video_rotA-090", 90);
331     trackNameToRotationDegrees.put("htc_video_rotA-180", 180);
332     trackNameToRotationDegrees.put("htc_video_rotA-270", 270);
333     TRACK_NAME_TO_ROTATION_DEGREES = Collections.unmodifiableMap(trackNameToRotationDegrees);
334   }
335 
336   private final EbmlReader reader;
337   private final VarintReader varintReader;
338   private final SparseArray<Track> tracks;
339   private final boolean seekForCuesEnabled;
340 
341   // Temporary arrays.
342   private final ParsableByteArray nalStartCode;
343   private final ParsableByteArray nalLength;
344   private final ParsableByteArray scratch;
345   private final ParsableByteArray vorbisNumPageSamples;
346   private final ParsableByteArray seekEntryIdBytes;
347   private final ParsableByteArray sampleStrippedBytes;
348   private final ParsableByteArray subtitleSample;
349   private final ParsableByteArray encryptionInitializationVector;
350   private final ParsableByteArray encryptionSubsampleData;
351   private final ParsableByteArray blockAdditionalData;
352   private ByteBuffer encryptionSubsampleDataBuffer;
353 
354   private long segmentContentSize;
355   private long segmentContentPosition = C.POSITION_UNSET;
356   private long timecodeScale = C.TIME_UNSET;
357   private long durationTimecode = C.TIME_UNSET;
358   private long durationUs = C.TIME_UNSET;
359 
360   // The track corresponding to the current TrackEntry element, or null.
361   @Nullable private Track currentTrack;
362 
363   // Whether a seek map has been sent to the output.
364   private boolean sentSeekMap;
365 
366   // Master seek entry related elements.
367   private int seekEntryId;
368   private long seekEntryPosition;
369 
370   // Cue related elements.
371   private boolean seekForCues;
372   private long cuesContentPosition = C.POSITION_UNSET;
373   private long seekPositionAfterBuildingCues = C.POSITION_UNSET;
374   private long clusterTimecodeUs = C.TIME_UNSET;
375   @Nullable private LongArray cueTimesUs;
376   @Nullable private LongArray cueClusterPositions;
377   private boolean seenClusterPositionForCurrentCuePoint;
378 
379   // Reading state.
380   private boolean haveOutputSample;
381 
382   // Block reading state.
383   private int blockState;
384   private long blockTimeUs;
385   private long blockDurationUs;
386   private int blockSampleIndex;
387   private int blockSampleCount;
388   private int[] blockSampleSizes;
389   private int blockTrackNumber;
390   private int blockTrackNumberLength;
391   @C.BufferFlags private int blockFlags;
392   private int blockAdditionalId;
393   private boolean blockHasReferenceBlock;
394 
395   // Sample writing state.
396   private int sampleBytesRead;
397   private int sampleBytesWritten;
398   private int sampleCurrentNalBytesRemaining;
399   private boolean sampleEncodingHandled;
400   private boolean sampleSignalByteRead;
401   private boolean samplePartitionCountRead;
402   private int samplePartitionCount;
403   private byte sampleSignalByte;
404   private boolean sampleInitializationVectorRead;
405 
406   // Extractor outputs.
407   private @MonotonicNonNull ExtractorOutput extractorOutput;
408 
MatroskaExtractor()409   public MatroskaExtractor() {
410     this(0);
411   }
412 
MatroskaExtractor(@lags int flags)413   public MatroskaExtractor(@Flags int flags) {
414     this(new DefaultEbmlReader(), flags);
415   }
416 
MatroskaExtractor(EbmlReader reader, @Flags int flags)417   /* package */ MatroskaExtractor(EbmlReader reader, @Flags int flags) {
418     this.reader = reader;
419     this.reader.init(new InnerEbmlProcessor());
420     seekForCuesEnabled = (flags & FLAG_DISABLE_SEEK_FOR_CUES) == 0;
421     varintReader = new VarintReader();
422     tracks = new SparseArray<>();
423     scratch = new ParsableByteArray(4);
424     vorbisNumPageSamples = new ParsableByteArray(ByteBuffer.allocate(4).putInt(-1).array());
425     seekEntryIdBytes = new ParsableByteArray(4);
426     nalStartCode = new ParsableByteArray(NalUnitUtil.NAL_START_CODE);
427     nalLength = new ParsableByteArray(4);
428     sampleStrippedBytes = new ParsableByteArray();
429     subtitleSample = new ParsableByteArray();
430     encryptionInitializationVector = new ParsableByteArray(ENCRYPTION_IV_SIZE);
431     encryptionSubsampleData = new ParsableByteArray();
432     blockAdditionalData = new ParsableByteArray();
433     blockSampleSizes = new int[1];
434   }
435 
436   @Override
sniff(ExtractorInput input)437   public final boolean sniff(ExtractorInput input) throws IOException {
438     return new Sniffer().sniff(input);
439   }
440 
441   @Override
init(ExtractorOutput output)442   public final void init(ExtractorOutput output) {
443     extractorOutput = output;
444   }
445 
446   @CallSuper
447   @Override
seek(long position, long timeUs)448   public void seek(long position, long timeUs) {
449     clusterTimecodeUs = C.TIME_UNSET;
450     blockState = BLOCK_STATE_START;
451     reader.reset();
452     varintReader.reset();
453     resetWriteSampleData();
454     for (int i = 0; i < tracks.size(); i++) {
455       tracks.valueAt(i).reset();
456     }
457   }
458 
459   @Override
release()460   public final void release() {
461     // Do nothing
462   }
463 
464   @Override
read(ExtractorInput input, PositionHolder seekPosition)465   public final int read(ExtractorInput input, PositionHolder seekPosition) throws IOException {
466     haveOutputSample = false;
467     boolean continueReading = true;
468     while (continueReading && !haveOutputSample) {
469       continueReading = reader.read(input);
470       if (continueReading && maybeSeekForCues(seekPosition, input.getPosition())) {
471         return Extractor.RESULT_SEEK;
472       }
473     }
474     if (!continueReading) {
475       for (int i = 0; i < tracks.size(); i++) {
476         tracks.valueAt(i).outputPendingSampleMetadata();
477       }
478       return Extractor.RESULT_END_OF_INPUT;
479     }
480     return Extractor.RESULT_CONTINUE;
481   }
482 
483   /**
484    * Maps an element ID to a corresponding type.
485    *
486    * @see EbmlProcessor#getElementType(int)
487    */
488   @CallSuper
489   @EbmlProcessor.ElementType
getElementType(int id)490   protected int getElementType(int id) {
491     switch (id) {
492       case ID_EBML:
493       case ID_SEGMENT:
494       case ID_SEEK_HEAD:
495       case ID_SEEK:
496       case ID_INFO:
497       case ID_CLUSTER:
498       case ID_TRACKS:
499       case ID_TRACK_ENTRY:
500       case ID_AUDIO:
501       case ID_VIDEO:
502       case ID_CONTENT_ENCODINGS:
503       case ID_CONTENT_ENCODING:
504       case ID_CONTENT_COMPRESSION:
505       case ID_CONTENT_ENCRYPTION:
506       case ID_CONTENT_ENCRYPTION_AES_SETTINGS:
507       case ID_CUES:
508       case ID_CUE_POINT:
509       case ID_CUE_TRACK_POSITIONS:
510       case ID_BLOCK_GROUP:
511       case ID_BLOCK_ADDITIONS:
512       case ID_BLOCK_MORE:
513       case ID_PROJECTION:
514       case ID_COLOUR:
515       case ID_MASTERING_METADATA:
516         return EbmlProcessor.ELEMENT_TYPE_MASTER;
517       case ID_EBML_READ_VERSION:
518       case ID_DOC_TYPE_READ_VERSION:
519       case ID_SEEK_POSITION:
520       case ID_TIMECODE_SCALE:
521       case ID_TIME_CODE:
522       case ID_BLOCK_DURATION:
523       case ID_PIXEL_WIDTH:
524       case ID_PIXEL_HEIGHT:
525       case ID_DISPLAY_WIDTH:
526       case ID_DISPLAY_HEIGHT:
527       case ID_DISPLAY_UNIT:
528       case ID_TRACK_NUMBER:
529       case ID_TRACK_TYPE:
530       case ID_FLAG_DEFAULT:
531       case ID_FLAG_FORCED:
532       case ID_DEFAULT_DURATION:
533       case ID_MAX_BLOCK_ADDITION_ID:
534       case ID_CODEC_DELAY:
535       case ID_SEEK_PRE_ROLL:
536       case ID_CHANNELS:
537       case ID_AUDIO_BIT_DEPTH:
538       case ID_CONTENT_ENCODING_ORDER:
539       case ID_CONTENT_ENCODING_SCOPE:
540       case ID_CONTENT_COMPRESSION_ALGORITHM:
541       case ID_CONTENT_ENCRYPTION_ALGORITHM:
542       case ID_CONTENT_ENCRYPTION_AES_SETTINGS_CIPHER_MODE:
543       case ID_CUE_TIME:
544       case ID_CUE_CLUSTER_POSITION:
545       case ID_REFERENCE_BLOCK:
546       case ID_STEREO_MODE:
547       case ID_COLOUR_RANGE:
548       case ID_COLOUR_TRANSFER:
549       case ID_COLOUR_PRIMARIES:
550       case ID_MAX_CLL:
551       case ID_MAX_FALL:
552       case ID_PROJECTION_TYPE:
553       case ID_BLOCK_ADD_ID:
554         return EbmlProcessor.ELEMENT_TYPE_UNSIGNED_INT;
555       case ID_DOC_TYPE:
556       case ID_NAME:
557       case ID_CODEC_ID:
558       case ID_LANGUAGE:
559         return EbmlProcessor.ELEMENT_TYPE_STRING;
560       case ID_SEEK_ID:
561       case ID_CONTENT_COMPRESSION_SETTINGS:
562       case ID_CONTENT_ENCRYPTION_KEY_ID:
563       case ID_SIMPLE_BLOCK:
564       case ID_BLOCK:
565       case ID_CODEC_PRIVATE:
566       case ID_PROJECTION_PRIVATE:
567       case ID_BLOCK_ADDITIONAL:
568         return EbmlProcessor.ELEMENT_TYPE_BINARY;
569       case ID_DURATION:
570       case ID_SAMPLING_FREQUENCY:
571       case ID_PRIMARY_R_CHROMATICITY_X:
572       case ID_PRIMARY_R_CHROMATICITY_Y:
573       case ID_PRIMARY_G_CHROMATICITY_X:
574       case ID_PRIMARY_G_CHROMATICITY_Y:
575       case ID_PRIMARY_B_CHROMATICITY_X:
576       case ID_PRIMARY_B_CHROMATICITY_Y:
577       case ID_WHITE_POINT_CHROMATICITY_X:
578       case ID_WHITE_POINT_CHROMATICITY_Y:
579       case ID_LUMNINANCE_MAX:
580       case ID_LUMNINANCE_MIN:
581       case ID_PROJECTION_POSE_YAW:
582       case ID_PROJECTION_POSE_PITCH:
583       case ID_PROJECTION_POSE_ROLL:
584         return EbmlProcessor.ELEMENT_TYPE_FLOAT;
585       default:
586         return EbmlProcessor.ELEMENT_TYPE_UNKNOWN;
587     }
588   }
589 
590   /**
591    * Checks if the given id is that of a level 1 element.
592    *
593    * @see EbmlProcessor#isLevel1Element(int)
594    */
595   @CallSuper
isLevel1Element(int id)596   protected boolean isLevel1Element(int id) {
597     return id == ID_SEGMENT_INFO || id == ID_CLUSTER || id == ID_CUES || id == ID_TRACKS;
598   }
599 
600   /**
601    * Called when the start of a master element is encountered.
602    *
603    * @see EbmlProcessor#startMasterElement(int, long, long)
604    */
605   @CallSuper
startMasterElement(int id, long contentPosition, long contentSize)606   protected void startMasterElement(int id, long contentPosition, long contentSize)
607       throws ParserException {
608     switch (id) {
609       case ID_SEGMENT:
610         if (segmentContentPosition != C.POSITION_UNSET
611             && segmentContentPosition != contentPosition) {
612           throw new ParserException("Multiple Segment elements not supported");
613         }
614         segmentContentPosition = contentPosition;
615         segmentContentSize = contentSize;
616         break;
617       case ID_SEEK:
618         seekEntryId = UNSET_ENTRY_ID;
619         seekEntryPosition = C.POSITION_UNSET;
620         break;
621       case ID_CUES:
622         cueTimesUs = new LongArray();
623         cueClusterPositions = new LongArray();
624         break;
625       case ID_CUE_POINT:
626         seenClusterPositionForCurrentCuePoint = false;
627         break;
628       case ID_CLUSTER:
629         if (!sentSeekMap) {
630           // We need to build cues before parsing the cluster.
631           if (seekForCuesEnabled && cuesContentPosition != C.POSITION_UNSET) {
632             // We know where the Cues element is located. Seek to request it.
633             seekForCues = true;
634           } else {
635             // We don't know where the Cues element is located. It's most likely omitted. Allow
636             // playback, but disable seeking.
637             extractorOutput.seekMap(new SeekMap.Unseekable(durationUs));
638             sentSeekMap = true;
639           }
640         }
641         break;
642       case ID_BLOCK_GROUP:
643         blockHasReferenceBlock = false;
644         break;
645       case ID_CONTENT_ENCODING:
646         // TODO: check and fail if more than one content encoding is present.
647         break;
648       case ID_CONTENT_ENCRYPTION:
649         currentTrack.hasContentEncryption = true;
650         break;
651       case ID_TRACK_ENTRY:
652         currentTrack = new Track();
653         break;
654       case ID_MASTERING_METADATA:
655         currentTrack.hasColorInfo = true;
656         break;
657       default:
658         break;
659     }
660   }
661 
662   /**
663    * Called when the end of a master element is encountered.
664    *
665    * @see EbmlProcessor#endMasterElement(int)
666    */
667   @CallSuper
endMasterElement(int id)668   protected void endMasterElement(int id) throws ParserException {
669     switch (id) {
670       case ID_SEGMENT_INFO:
671         if (timecodeScale == C.TIME_UNSET) {
672           // timecodeScale was omitted. Use the default value.
673           timecodeScale = 1000000;
674         }
675         if (durationTimecode != C.TIME_UNSET) {
676           durationUs = scaleTimecodeToUs(durationTimecode);
677         }
678         break;
679       case ID_SEEK:
680         if (seekEntryId == UNSET_ENTRY_ID || seekEntryPosition == C.POSITION_UNSET) {
681           throw new ParserException("Mandatory element SeekID or SeekPosition not found");
682         }
683         if (seekEntryId == ID_CUES) {
684           cuesContentPosition = seekEntryPosition;
685         }
686         break;
687       case ID_CUES:
688         if (!sentSeekMap) {
689           extractorOutput.seekMap(buildSeekMap());
690           sentSeekMap = true;
691         } else {
692           // We have already built the cues. Ignore.
693         }
694         break;
695       case ID_BLOCK_GROUP:
696         if (blockState != BLOCK_STATE_DATA) {
697           // We've skipped this block (due to incompatible track number).
698           return;
699         }
700         // Commit sample metadata.
701         int sampleOffset = 0;
702         for (int i = 0; i < blockSampleCount; i++) {
703           sampleOffset += blockSampleSizes[i];
704         }
705         Track track = tracks.get(blockTrackNumber);
706         for (int i = 0; i < blockSampleCount; i++) {
707           long sampleTimeUs = blockTimeUs + (i * track.defaultSampleDurationNs) / 1000;
708           int sampleFlags = blockFlags;
709           if (i == 0 && !blockHasReferenceBlock) {
710             // If the ReferenceBlock element was not found in this block, then the first frame is a
711             // keyframe.
712             sampleFlags |= C.BUFFER_FLAG_KEY_FRAME;
713           }
714           int sampleSize = blockSampleSizes[i];
715           sampleOffset -= sampleSize; // The offset is to the end of the sample.
716           commitSampleToOutput(track, sampleTimeUs, sampleFlags, sampleSize, sampleOffset);
717         }
718         blockState = BLOCK_STATE_START;
719         break;
720       case ID_CONTENT_ENCODING:
721         if (currentTrack.hasContentEncryption) {
722           if (currentTrack.cryptoData == null) {
723             throw new ParserException("Encrypted Track found but ContentEncKeyID was not found");
724           }
725           currentTrack.drmInitData = new DrmInitData(new SchemeData(C.UUID_NIL,
726               MimeTypes.VIDEO_WEBM, currentTrack.cryptoData.encryptionKey));
727         }
728         break;
729       case ID_CONTENT_ENCODINGS:
730         if (currentTrack.hasContentEncryption && currentTrack.sampleStrippedBytes != null) {
731           throw new ParserException("Combining encryption and compression is not supported");
732         }
733         break;
734       case ID_TRACK_ENTRY:
735         if (isCodecSupported(currentTrack.codecId)) {
736           currentTrack.initializeOutput(extractorOutput, currentTrack.number);
737           tracks.put(currentTrack.number, currentTrack);
738         }
739         currentTrack = null;
740         break;
741       case ID_TRACKS:
742         if (tracks.size() == 0) {
743           throw new ParserException("No valid tracks were found");
744         }
745         extractorOutput.endTracks();
746         break;
747       default:
748         break;
749     }
750   }
751 
752   /**
753    * Called when an integer element is encountered.
754    *
755    * @see EbmlProcessor#integerElement(int, long)
756    */
757   @CallSuper
integerElement(int id, long value)758   protected void integerElement(int id, long value) throws ParserException {
759     switch (id) {
760       case ID_EBML_READ_VERSION:
761         // Validate that EBMLReadVersion is supported. This extractor only supports v1.
762         if (value != 1) {
763           throw new ParserException("EBMLReadVersion " + value + " not supported");
764         }
765         break;
766       case ID_DOC_TYPE_READ_VERSION:
767         // Validate that DocTypeReadVersion is supported. This extractor only supports up to v2.
768         if (value < 1 || value > 2) {
769           throw new ParserException("DocTypeReadVersion " + value + " not supported");
770         }
771         break;
772       case ID_SEEK_POSITION:
773         // Seek Position is the relative offset beginning from the Segment. So to get absolute
774         // offset from the beginning of the file, we need to add segmentContentPosition to it.
775         seekEntryPosition = value + segmentContentPosition;
776         break;
777       case ID_TIMECODE_SCALE:
778         timecodeScale = value;
779         break;
780       case ID_PIXEL_WIDTH:
781         currentTrack.width = (int) value;
782         break;
783       case ID_PIXEL_HEIGHT:
784         currentTrack.height = (int) value;
785         break;
786       case ID_DISPLAY_WIDTH:
787         currentTrack.displayWidth = (int) value;
788         break;
789       case ID_DISPLAY_HEIGHT:
790         currentTrack.displayHeight = (int) value;
791         break;
792       case ID_DISPLAY_UNIT:
793         currentTrack.displayUnit = (int) value;
794         break;
795       case ID_TRACK_NUMBER:
796         currentTrack.number = (int) value;
797         break;
798       case ID_FLAG_DEFAULT:
799         currentTrack.flagDefault = value == 1;
800         break;
801       case ID_FLAG_FORCED:
802         currentTrack.flagForced = value == 1;
803         break;
804       case ID_TRACK_TYPE:
805         currentTrack.type = (int) value;
806         break;
807       case ID_DEFAULT_DURATION:
808         currentTrack.defaultSampleDurationNs = (int) value;
809         break;
810       case ID_MAX_BLOCK_ADDITION_ID:
811         currentTrack.maxBlockAdditionId = (int) value;
812         break;
813       case ID_CODEC_DELAY:
814         currentTrack.codecDelayNs = value;
815         break;
816       case ID_SEEK_PRE_ROLL:
817         currentTrack.seekPreRollNs = value;
818         break;
819       case ID_CHANNELS:
820         currentTrack.channelCount = (int) value;
821         break;
822       case ID_AUDIO_BIT_DEPTH:
823         currentTrack.audioBitDepth = (int) value;
824         break;
825       case ID_REFERENCE_BLOCK:
826         blockHasReferenceBlock = true;
827         break;
828       case ID_CONTENT_ENCODING_ORDER:
829         // This extractor only supports one ContentEncoding element and hence the order has to be 0.
830         if (value != 0) {
831           throw new ParserException("ContentEncodingOrder " + value + " not supported");
832         }
833         break;
834       case ID_CONTENT_ENCODING_SCOPE:
835         // This extractor only supports the scope of all frames.
836         if (value != 1) {
837           throw new ParserException("ContentEncodingScope " + value + " not supported");
838         }
839         break;
840       case ID_CONTENT_COMPRESSION_ALGORITHM:
841         // This extractor only supports header stripping.
842         if (value != 3) {
843           throw new ParserException("ContentCompAlgo " + value + " not supported");
844         }
845         break;
846       case ID_CONTENT_ENCRYPTION_ALGORITHM:
847         // Only the value 5 (AES) is allowed according to the WebM specification.
848         if (value != 5) {
849           throw new ParserException("ContentEncAlgo " + value + " not supported");
850         }
851         break;
852       case ID_CONTENT_ENCRYPTION_AES_SETTINGS_CIPHER_MODE:
853         // Only the value 1 is allowed according to the WebM specification.
854         if (value != 1) {
855           throw new ParserException("AESSettingsCipherMode " + value + " not supported");
856         }
857         break;
858       case ID_CUE_TIME:
859         cueTimesUs.add(scaleTimecodeToUs(value));
860         break;
861       case ID_CUE_CLUSTER_POSITION:
862         if (!seenClusterPositionForCurrentCuePoint) {
863           // If there's more than one video/audio track, then there could be more than one
864           // CueTrackPositions within a single CuePoint. In such a case, ignore all but the first
865           // one (since the cluster position will be quite close for all the tracks).
866           cueClusterPositions.add(value);
867           seenClusterPositionForCurrentCuePoint = true;
868         }
869         break;
870       case ID_TIME_CODE:
871         clusterTimecodeUs = scaleTimecodeToUs(value);
872         break;
873       case ID_BLOCK_DURATION:
874         blockDurationUs = scaleTimecodeToUs(value);
875         break;
876       case ID_STEREO_MODE:
877         int layout = (int) value;
878         switch (layout) {
879           case 0:
880             currentTrack.stereoMode = C.STEREO_MODE_MONO;
881             break;
882           case 1:
883             currentTrack.stereoMode = C.STEREO_MODE_LEFT_RIGHT;
884             break;
885           case 3:
886             currentTrack.stereoMode = C.STEREO_MODE_TOP_BOTTOM;
887             break;
888           case 15:
889             currentTrack.stereoMode = C.STEREO_MODE_STEREO_MESH;
890             break;
891           default:
892             break;
893         }
894         break;
895       case ID_COLOUR_PRIMARIES:
896         currentTrack.hasColorInfo = true;
897         switch ((int) value) {
898           case 1:
899             currentTrack.colorSpace = C.COLOR_SPACE_BT709;
900             break;
901           case 4:  // BT.470M.
902           case 5:  // BT.470BG.
903           case 6:  // SMPTE 170M.
904           case 7:  // SMPTE 240M.
905             currentTrack.colorSpace = C.COLOR_SPACE_BT601;
906             break;
907           case 9:
908             currentTrack.colorSpace = C.COLOR_SPACE_BT2020;
909             break;
910           default:
911             break;
912         }
913         break;
914       case ID_COLOUR_TRANSFER:
915         switch ((int) value) {
916           case 1:  // BT.709.
917           case 6:  // SMPTE 170M.
918           case 7:  // SMPTE 240M.
919             currentTrack.colorTransfer = C.COLOR_TRANSFER_SDR;
920             break;
921           case 16:
922             currentTrack.colorTransfer = C.COLOR_TRANSFER_ST2084;
923             break;
924           case 18:
925             currentTrack.colorTransfer = C.COLOR_TRANSFER_HLG;
926             break;
927           default:
928             break;
929         }
930         break;
931       case ID_COLOUR_RANGE:
932         switch((int) value) {
933           case 1:  // Broadcast range.
934             currentTrack.colorRange = C.COLOR_RANGE_LIMITED;
935             break;
936           case 2:
937             currentTrack.colorRange = C.COLOR_RANGE_FULL;
938             break;
939           default:
940             break;
941         }
942         break;
943       case ID_MAX_CLL:
944         currentTrack.maxContentLuminance = (int) value;
945         break;
946       case ID_MAX_FALL:
947         currentTrack.maxFrameAverageLuminance = (int) value;
948         break;
949       case ID_PROJECTION_TYPE:
950         switch ((int) value) {
951           case 0:
952             currentTrack.projectionType = C.PROJECTION_RECTANGULAR;
953             break;
954           case 1:
955             currentTrack.projectionType = C.PROJECTION_EQUIRECTANGULAR;
956             break;
957           case 2:
958             currentTrack.projectionType = C.PROJECTION_CUBEMAP;
959             break;
960           case 3:
961             currentTrack.projectionType = C.PROJECTION_MESH;
962             break;
963           default:
964             break;
965         }
966         break;
967       case ID_BLOCK_ADD_ID:
968         blockAdditionalId = (int) value;
969         break;
970       default:
971         break;
972     }
973   }
974 
975   /**
976    * Called when a float element is encountered.
977    *
978    * @see EbmlProcessor#floatElement(int, double)
979    */
980   @CallSuper
floatElement(int id, double value)981   protected void floatElement(int id, double value) throws ParserException {
982     switch (id) {
983       case ID_DURATION:
984         durationTimecode = (long) value;
985         break;
986       case ID_SAMPLING_FREQUENCY:
987         currentTrack.sampleRate = (int) value;
988         break;
989       case ID_PRIMARY_R_CHROMATICITY_X:
990         currentTrack.primaryRChromaticityX = (float) value;
991         break;
992       case ID_PRIMARY_R_CHROMATICITY_Y:
993         currentTrack.primaryRChromaticityY = (float) value;
994         break;
995       case ID_PRIMARY_G_CHROMATICITY_X:
996         currentTrack.primaryGChromaticityX = (float) value;
997         break;
998       case ID_PRIMARY_G_CHROMATICITY_Y:
999         currentTrack.primaryGChromaticityY = (float) value;
1000         break;
1001       case ID_PRIMARY_B_CHROMATICITY_X:
1002         currentTrack.primaryBChromaticityX = (float) value;
1003         break;
1004       case ID_PRIMARY_B_CHROMATICITY_Y:
1005         currentTrack.primaryBChromaticityY = (float) value;
1006         break;
1007       case ID_WHITE_POINT_CHROMATICITY_X:
1008         currentTrack.whitePointChromaticityX = (float) value;
1009         break;
1010       case ID_WHITE_POINT_CHROMATICITY_Y:
1011         currentTrack.whitePointChromaticityY = (float) value;
1012         break;
1013       case ID_LUMNINANCE_MAX:
1014         currentTrack.maxMasteringLuminance = (float) value;
1015         break;
1016       case ID_LUMNINANCE_MIN:
1017         currentTrack.minMasteringLuminance = (float) value;
1018         break;
1019       case ID_PROJECTION_POSE_YAW:
1020         currentTrack.projectionPoseYaw = (float) value;
1021         break;
1022       case ID_PROJECTION_POSE_PITCH:
1023         currentTrack.projectionPosePitch = (float) value;
1024         break;
1025       case ID_PROJECTION_POSE_ROLL:
1026         currentTrack.projectionPoseRoll = (float) value;
1027         break;
1028       default:
1029         break;
1030     }
1031   }
1032 
1033   /**
1034    * Called when a string element is encountered.
1035    *
1036    * @see EbmlProcessor#stringElement(int, String)
1037    */
1038   @CallSuper
stringElement(int id, String value)1039   protected void stringElement(int id, String value) throws ParserException {
1040     switch (id) {
1041       case ID_DOC_TYPE:
1042         // Validate that DocType is supported.
1043         if (!DOC_TYPE_WEBM.equals(value) && !DOC_TYPE_MATROSKA.equals(value)) {
1044           throw new ParserException("DocType " + value + " not supported");
1045         }
1046         break;
1047       case ID_NAME:
1048         currentTrack.name = value;
1049         break;
1050       case ID_CODEC_ID:
1051         currentTrack.codecId = value;
1052         break;
1053       case ID_LANGUAGE:
1054         currentTrack.language = value;
1055         break;
1056       default:
1057         break;
1058     }
1059   }
1060 
1061   /**
1062    * Called when a binary element is encountered.
1063    *
1064    * @see EbmlProcessor#binaryElement(int, int, ExtractorInput)
1065    */
1066   @CallSuper
binaryElement(int id, int contentSize, ExtractorInput input)1067   protected void binaryElement(int id, int contentSize, ExtractorInput input) throws IOException {
1068     switch (id) {
1069       case ID_SEEK_ID:
1070         Arrays.fill(seekEntryIdBytes.data, (byte) 0);
1071         input.readFully(seekEntryIdBytes.data, 4 - contentSize, contentSize);
1072         seekEntryIdBytes.setPosition(0);
1073         seekEntryId = (int) seekEntryIdBytes.readUnsignedInt();
1074         break;
1075       case ID_CODEC_PRIVATE:
1076         currentTrack.codecPrivate = new byte[contentSize];
1077         input.readFully(currentTrack.codecPrivate, 0, contentSize);
1078         break;
1079       case ID_PROJECTION_PRIVATE:
1080         currentTrack.projectionData = new byte[contentSize];
1081         input.readFully(currentTrack.projectionData, 0, contentSize);
1082         break;
1083       case ID_CONTENT_COMPRESSION_SETTINGS:
1084         // This extractor only supports header stripping, so the payload is the stripped bytes.
1085         currentTrack.sampleStrippedBytes = new byte[contentSize];
1086         input.readFully(currentTrack.sampleStrippedBytes, 0, contentSize);
1087         break;
1088       case ID_CONTENT_ENCRYPTION_KEY_ID:
1089         byte[] encryptionKey = new byte[contentSize];
1090         input.readFully(encryptionKey, 0, contentSize);
1091         currentTrack.cryptoData = new TrackOutput.CryptoData(C.CRYPTO_MODE_AES_CTR, encryptionKey,
1092             0, 0); // We assume patternless AES-CTR.
1093         break;
1094       case ID_SIMPLE_BLOCK:
1095       case ID_BLOCK:
1096         // Please refer to http://www.matroska.org/technical/specs/index.html#simpleblock_structure
1097         // and http://matroska.org/technical/specs/index.html#block_structure
1098         // for info about how data is organized in SimpleBlock and Block elements respectively. They
1099         // differ only in the way flags are specified.
1100 
1101         if (blockState == BLOCK_STATE_START) {
1102           blockTrackNumber = (int) varintReader.readUnsignedVarint(input, false, true, 8);
1103           blockTrackNumberLength = varintReader.getLastLength();
1104           blockDurationUs = C.TIME_UNSET;
1105           blockState = BLOCK_STATE_HEADER;
1106           scratch.reset();
1107         }
1108 
1109         Track track = tracks.get(blockTrackNumber);
1110 
1111         // Ignore the block if we don't know about the track to which it belongs.
1112         if (track == null) {
1113           input.skipFully(contentSize - blockTrackNumberLength);
1114           blockState = BLOCK_STATE_START;
1115           return;
1116         }
1117 
1118         if (blockState == BLOCK_STATE_HEADER) {
1119           // Read the relative timecode (2 bytes) and flags (1 byte).
1120           readScratch(input, 3);
1121           int lacing = (scratch.data[2] & 0x06) >> 1;
1122           if (lacing == LACING_NONE) {
1123             blockSampleCount = 1;
1124             blockSampleSizes = ensureArrayCapacity(blockSampleSizes, 1);
1125             blockSampleSizes[0] = contentSize - blockTrackNumberLength - 3;
1126           } else {
1127             // Read the sample count (1 byte).
1128             readScratch(input, 4);
1129             blockSampleCount = (scratch.data[3] & 0xFF) + 1;
1130             blockSampleSizes = ensureArrayCapacity(blockSampleSizes, blockSampleCount);
1131             if (lacing == LACING_FIXED_SIZE) {
1132               int blockLacingSampleSize =
1133                   (contentSize - blockTrackNumberLength - 4) / blockSampleCount;
1134               Arrays.fill(blockSampleSizes, 0, blockSampleCount, blockLacingSampleSize);
1135             } else if (lacing == LACING_XIPH) {
1136               int totalSamplesSize = 0;
1137               int headerSize = 4;
1138               for (int sampleIndex = 0; sampleIndex < blockSampleCount - 1; sampleIndex++) {
1139                 blockSampleSizes[sampleIndex] = 0;
1140                 int byteValue;
1141                 do {
1142                   readScratch(input, ++headerSize);
1143                   byteValue = scratch.data[headerSize - 1] & 0xFF;
1144                   blockSampleSizes[sampleIndex] += byteValue;
1145                 } while (byteValue == 0xFF);
1146                 totalSamplesSize += blockSampleSizes[sampleIndex];
1147               }
1148               blockSampleSizes[blockSampleCount - 1] =
1149                   contentSize - blockTrackNumberLength - headerSize - totalSamplesSize;
1150             } else if (lacing == LACING_EBML) {
1151               int totalSamplesSize = 0;
1152               int headerSize = 4;
1153               for (int sampleIndex = 0; sampleIndex < blockSampleCount - 1; sampleIndex++) {
1154                 blockSampleSizes[sampleIndex] = 0;
1155                 readScratch(input, ++headerSize);
1156                 if (scratch.data[headerSize - 1] == 0) {
1157                   throw new ParserException("No valid varint length mask found");
1158                 }
1159                 long readValue = 0;
1160                 for (int i = 0; i < 8; i++) {
1161                   int lengthMask = 1 << (7 - i);
1162                   if ((scratch.data[headerSize - 1] & lengthMask) != 0) {
1163                     int readPosition = headerSize - 1;
1164                     headerSize += i;
1165                     readScratch(input, headerSize);
1166                     readValue = (scratch.data[readPosition++] & 0xFF) & ~lengthMask;
1167                     while (readPosition < headerSize) {
1168                       readValue <<= 8;
1169                       readValue |= (scratch.data[readPosition++] & 0xFF);
1170                     }
1171                     // The first read value is the first size. Later values are signed offsets.
1172                     if (sampleIndex > 0) {
1173                       readValue -= (1L << (6 + i * 7)) - 1;
1174                     }
1175                     break;
1176                   }
1177                 }
1178                 if (readValue < Integer.MIN_VALUE || readValue > Integer.MAX_VALUE) {
1179                   throw new ParserException("EBML lacing sample size out of range.");
1180                 }
1181                 int intReadValue = (int) readValue;
1182                 blockSampleSizes[sampleIndex] =
1183                     sampleIndex == 0
1184                         ? intReadValue
1185                         : blockSampleSizes[sampleIndex - 1] + intReadValue;
1186                 totalSamplesSize += blockSampleSizes[sampleIndex];
1187               }
1188               blockSampleSizes[blockSampleCount - 1] =
1189                   contentSize - blockTrackNumberLength - headerSize - totalSamplesSize;
1190             } else {
1191               // Lacing is always in the range 0--3.
1192               throw new ParserException("Unexpected lacing value: " + lacing);
1193             }
1194           }
1195 
1196           int timecode = (scratch.data[0] << 8) | (scratch.data[1] & 0xFF);
1197           blockTimeUs = clusterTimecodeUs + scaleTimecodeToUs(timecode);
1198           boolean isInvisible = (scratch.data[2] & 0x08) == 0x08;
1199           boolean isKeyframe = track.type == TRACK_TYPE_AUDIO
1200               || (id == ID_SIMPLE_BLOCK && (scratch.data[2] & 0x80) == 0x80);
1201           blockFlags = (isKeyframe ? C.BUFFER_FLAG_KEY_FRAME : 0)
1202               | (isInvisible ? C.BUFFER_FLAG_DECODE_ONLY : 0);
1203           blockState = BLOCK_STATE_DATA;
1204           blockSampleIndex = 0;
1205         }
1206 
1207         if (id == ID_SIMPLE_BLOCK) {
1208           // For SimpleBlock, we can write sample data and immediately commit the corresponding
1209           // sample metadata.
1210           while (blockSampleIndex < blockSampleCount) {
1211             int sampleSize = writeSampleData(input, track, blockSampleSizes[blockSampleIndex]);
1212             long sampleTimeUs =
1213                 blockTimeUs + (blockSampleIndex * track.defaultSampleDurationNs) / 1000;
1214             commitSampleToOutput(track, sampleTimeUs, blockFlags, sampleSize, /* offset= */ 0);
1215             blockSampleIndex++;
1216           }
1217           blockState = BLOCK_STATE_START;
1218         } else {
1219           // For Block, we need to wait until the end of the BlockGroup element before committing
1220           // sample metadata. This is so that we can handle ReferenceBlock (which can be used to
1221           // infer whether the first sample in the block is a keyframe), and BlockAdditions (which
1222           // can contain additional sample data to append) contained in the block group. Just output
1223           // the sample data, storing the final sample sizes for when we commit the metadata.
1224           while (blockSampleIndex < blockSampleCount) {
1225             blockSampleSizes[blockSampleIndex] =
1226                 writeSampleData(input, track, blockSampleSizes[blockSampleIndex]);
1227             blockSampleIndex++;
1228           }
1229         }
1230 
1231         break;
1232       case ID_BLOCK_ADDITIONAL:
1233         if (blockState != BLOCK_STATE_DATA) {
1234           return;
1235         }
1236         handleBlockAdditionalData(
1237             tracks.get(blockTrackNumber), blockAdditionalId, input, contentSize);
1238         break;
1239       default:
1240         throw new ParserException("Unexpected id: " + id);
1241     }
1242   }
1243 
handleBlockAdditionalData( Track track, int blockAdditionalId, ExtractorInput input, int contentSize)1244   protected void handleBlockAdditionalData(
1245       Track track, int blockAdditionalId, ExtractorInput input, int contentSize)
1246       throws IOException {
1247     if (blockAdditionalId == BLOCK_ADDITIONAL_ID_VP9_ITU_T_35
1248         && CODEC_ID_VP9.equals(track.codecId)) {
1249       blockAdditionalData.reset(contentSize);
1250       input.readFully(blockAdditionalData.data, 0, contentSize);
1251     } else {
1252       // Unhandled block additional data.
1253       input.skipFully(contentSize);
1254     }
1255   }
1256 
commitSampleToOutput( Track track, long timeUs, @C.BufferFlags int flags, int size, int offset)1257   private void commitSampleToOutput(
1258       Track track, long timeUs, @C.BufferFlags int flags, int size, int offset) {
1259     if (track.trueHdSampleRechunker != null) {
1260       track.trueHdSampleRechunker.sampleMetadata(track, timeUs, flags, size, offset);
1261     } else {
1262       if (CODEC_ID_SUBRIP.equals(track.codecId) || CODEC_ID_ASS.equals(track.codecId)) {
1263         if (blockSampleCount > 1) {
1264           Log.w(TAG, "Skipping subtitle sample in laced block.");
1265         } else if (blockDurationUs == C.TIME_UNSET) {
1266           Log.w(TAG, "Skipping subtitle sample with no duration.");
1267         } else {
1268           setSubtitleEndTime(track.codecId, blockDurationUs, subtitleSample.data);
1269           // Note: If we ever want to support DRM protected subtitles then we'll need to output the
1270           // appropriate encryption data here.
1271           track.output.sampleData(subtitleSample, subtitleSample.limit());
1272           size += subtitleSample.limit();
1273         }
1274       }
1275 
1276       if ((flags & C.BUFFER_FLAG_HAS_SUPPLEMENTAL_DATA) != 0) {
1277         if (blockSampleCount > 1) {
1278           // There were multiple samples in the block. Appending the additional data to the last
1279           // sample doesn't make sense. Skip instead.
1280           flags &= ~C.BUFFER_FLAG_HAS_SUPPLEMENTAL_DATA;
1281         } else {
1282           // Append supplemental data.
1283           int blockAdditionalSize = blockAdditionalData.limit();
1284           track.output.sampleData(
1285               blockAdditionalData, blockAdditionalSize, TrackOutput.SAMPLE_DATA_PART_SUPPLEMENTAL);
1286           size += blockAdditionalSize;
1287         }
1288       }
1289       track.output.sampleMetadata(timeUs, flags, size, offset, track.cryptoData);
1290     }
1291     haveOutputSample = true;
1292   }
1293 
1294   /**
1295    * Ensures {@link #scratch} contains at least {@code requiredLength} bytes of data, reading from
1296    * the extractor input if necessary.
1297    */
readScratch(ExtractorInput input, int requiredLength)1298   private void readScratch(ExtractorInput input, int requiredLength) throws IOException {
1299     if (scratch.limit() >= requiredLength) {
1300       return;
1301     }
1302     if (scratch.capacity() < requiredLength) {
1303       scratch.reset(Arrays.copyOf(scratch.data, Math.max(scratch.data.length * 2, requiredLength)),
1304           scratch.limit());
1305     }
1306     input.readFully(scratch.data, scratch.limit(), requiredLength - scratch.limit());
1307     scratch.setLimit(requiredLength);
1308   }
1309 
1310   /**
1311    * Writes data for a single sample to the track output.
1312    *
1313    * @param input The input from which to read sample data.
1314    * @param track The track to output the sample to.
1315    * @param size The size of the sample data on the input side.
1316    * @return The final size of the written sample.
1317    * @throws IOException If an error occurs reading from the input.
1318    */
writeSampleData(ExtractorInput input, Track track, int size)1319   private int writeSampleData(ExtractorInput input, Track track, int size) throws IOException {
1320     if (CODEC_ID_SUBRIP.equals(track.codecId)) {
1321       writeSubtitleSampleData(input, SUBRIP_PREFIX, size);
1322       return finishWriteSampleData();
1323     } else if (CODEC_ID_ASS.equals(track.codecId)) {
1324       writeSubtitleSampleData(input, SSA_PREFIX, size);
1325       return finishWriteSampleData();
1326     }
1327 
1328     TrackOutput output = track.output;
1329     if (!sampleEncodingHandled) {
1330       if (track.hasContentEncryption) {
1331         // If the sample is encrypted, read its encryption signal byte and set the IV size.
1332         // Clear the encrypted flag.
1333         blockFlags &= ~C.BUFFER_FLAG_ENCRYPTED;
1334         if (!sampleSignalByteRead) {
1335           input.readFully(scratch.data, 0, 1);
1336           sampleBytesRead++;
1337           if ((scratch.data[0] & 0x80) == 0x80) {
1338             throw new ParserException("Extension bit is set in signal byte");
1339           }
1340           sampleSignalByte = scratch.data[0];
1341           sampleSignalByteRead = true;
1342         }
1343         boolean isEncrypted = (sampleSignalByte & 0x01) == 0x01;
1344         if (isEncrypted) {
1345           boolean hasSubsampleEncryption = (sampleSignalByte & 0x02) == 0x02;
1346           blockFlags |= C.BUFFER_FLAG_ENCRYPTED;
1347           if (!sampleInitializationVectorRead) {
1348             input.readFully(encryptionInitializationVector.data, 0, ENCRYPTION_IV_SIZE);
1349             sampleBytesRead += ENCRYPTION_IV_SIZE;
1350             sampleInitializationVectorRead = true;
1351             // Write the signal byte, containing the IV size and the subsample encryption flag.
1352             scratch.data[0] = (byte) (ENCRYPTION_IV_SIZE | (hasSubsampleEncryption ? 0x80 : 0x00));
1353             scratch.setPosition(0);
1354             output.sampleData(scratch, 1, TrackOutput.SAMPLE_DATA_PART_ENCRYPTION);
1355             sampleBytesWritten++;
1356             // Write the IV.
1357             encryptionInitializationVector.setPosition(0);
1358             output.sampleData(
1359                 encryptionInitializationVector,
1360                 ENCRYPTION_IV_SIZE,
1361                 TrackOutput.SAMPLE_DATA_PART_ENCRYPTION);
1362             sampleBytesWritten += ENCRYPTION_IV_SIZE;
1363           }
1364           if (hasSubsampleEncryption) {
1365             if (!samplePartitionCountRead) {
1366               input.readFully(scratch.data, 0, 1);
1367               sampleBytesRead++;
1368               scratch.setPosition(0);
1369               samplePartitionCount = scratch.readUnsignedByte();
1370               samplePartitionCountRead = true;
1371             }
1372             int samplePartitionDataSize = samplePartitionCount * 4;
1373             scratch.reset(samplePartitionDataSize);
1374             input.readFully(scratch.data, 0, samplePartitionDataSize);
1375             sampleBytesRead += samplePartitionDataSize;
1376             short subsampleCount = (short) (1 + (samplePartitionCount / 2));
1377             int subsampleDataSize = 2 + 6 * subsampleCount;
1378             if (encryptionSubsampleDataBuffer == null
1379                 || encryptionSubsampleDataBuffer.capacity() < subsampleDataSize) {
1380               encryptionSubsampleDataBuffer = ByteBuffer.allocate(subsampleDataSize);
1381             }
1382             encryptionSubsampleDataBuffer.position(0);
1383             encryptionSubsampleDataBuffer.putShort(subsampleCount);
1384             // Loop through the partition offsets and write out the data in the way ExoPlayer
1385             // wants it (ISO 23001-7 Part 7):
1386             //   2 bytes - sub sample count.
1387             //   for each sub sample:
1388             //     2 bytes - clear data size.
1389             //     4 bytes - encrypted data size.
1390             int partitionOffset = 0;
1391             for (int i = 0; i < samplePartitionCount; i++) {
1392               int previousPartitionOffset = partitionOffset;
1393               partitionOffset = scratch.readUnsignedIntToInt();
1394               if ((i % 2) == 0) {
1395                 encryptionSubsampleDataBuffer.putShort(
1396                     (short) (partitionOffset - previousPartitionOffset));
1397               } else {
1398                 encryptionSubsampleDataBuffer.putInt(partitionOffset - previousPartitionOffset);
1399               }
1400             }
1401             int finalPartitionSize = size - sampleBytesRead - partitionOffset;
1402             if ((samplePartitionCount % 2) == 1) {
1403               encryptionSubsampleDataBuffer.putInt(finalPartitionSize);
1404             } else {
1405               encryptionSubsampleDataBuffer.putShort((short) finalPartitionSize);
1406               encryptionSubsampleDataBuffer.putInt(0);
1407             }
1408             encryptionSubsampleData.reset(encryptionSubsampleDataBuffer.array(), subsampleDataSize);
1409             output.sampleData(
1410                 encryptionSubsampleData,
1411                 subsampleDataSize,
1412                 TrackOutput.SAMPLE_DATA_PART_ENCRYPTION);
1413             sampleBytesWritten += subsampleDataSize;
1414           }
1415         }
1416       } else if (track.sampleStrippedBytes != null) {
1417         // If the sample has header stripping, prepare to read/output the stripped bytes first.
1418         sampleStrippedBytes.reset(track.sampleStrippedBytes, track.sampleStrippedBytes.length);
1419       }
1420 
1421       if (track.maxBlockAdditionId > 0) {
1422         blockFlags |= C.BUFFER_FLAG_HAS_SUPPLEMENTAL_DATA;
1423         blockAdditionalData.reset();
1424         // If there is supplemental data, the structure of the sample data is:
1425         // sample size (4 bytes) || sample data || supplemental data
1426         scratch.reset(/* limit= */ 4);
1427         scratch.data[0] = (byte) ((size >> 24) & 0xFF);
1428         scratch.data[1] = (byte) ((size >> 16) & 0xFF);
1429         scratch.data[2] = (byte) ((size >> 8) & 0xFF);
1430         scratch.data[3] = (byte) (size & 0xFF);
1431         output.sampleData(scratch, 4, TrackOutput.SAMPLE_DATA_PART_SUPPLEMENTAL);
1432         sampleBytesWritten += 4;
1433       }
1434 
1435       sampleEncodingHandled = true;
1436     }
1437     size += sampleStrippedBytes.limit();
1438 
1439     if (CODEC_ID_H264.equals(track.codecId) || CODEC_ID_H265.equals(track.codecId)) {
1440       // TODO: Deduplicate with Mp4Extractor.
1441 
1442       // Zero the top three bytes of the array that we'll use to decode nal unit lengths, in case
1443       // they're only 1 or 2 bytes long.
1444       byte[] nalLengthData = nalLength.data;
1445       nalLengthData[0] = 0;
1446       nalLengthData[1] = 0;
1447       nalLengthData[2] = 0;
1448       int nalUnitLengthFieldLength = track.nalUnitLengthFieldLength;
1449       int nalUnitLengthFieldLengthDiff = 4 - track.nalUnitLengthFieldLength;
1450       // NAL units are length delimited, but the decoder requires start code delimited units.
1451       // Loop until we've written the sample to the track output, replacing length delimiters with
1452       // start codes as we encounter them.
1453       while (sampleBytesRead < size) {
1454         if (sampleCurrentNalBytesRemaining == 0) {
1455           // Read the NAL length so that we know where we find the next one.
1456           writeToTarget(
1457               input, nalLengthData, nalUnitLengthFieldLengthDiff, nalUnitLengthFieldLength);
1458           sampleBytesRead += nalUnitLengthFieldLength;
1459           nalLength.setPosition(0);
1460           sampleCurrentNalBytesRemaining = nalLength.readUnsignedIntToInt();
1461           // Write a start code for the current NAL unit.
1462           nalStartCode.setPosition(0);
1463           output.sampleData(nalStartCode, 4);
1464           sampleBytesWritten += 4;
1465         } else {
1466           // Write the payload of the NAL unit.
1467           int bytesWritten = writeToOutput(input, output, sampleCurrentNalBytesRemaining);
1468           sampleBytesRead += bytesWritten;
1469           sampleBytesWritten += bytesWritten;
1470           sampleCurrentNalBytesRemaining -= bytesWritten;
1471         }
1472       }
1473     } else {
1474       if (track.trueHdSampleRechunker != null) {
1475         Assertions.checkState(sampleStrippedBytes.limit() == 0);
1476         track.trueHdSampleRechunker.startSample(input);
1477       }
1478       while (sampleBytesRead < size) {
1479         int bytesWritten = writeToOutput(input, output, size - sampleBytesRead);
1480         sampleBytesRead += bytesWritten;
1481         sampleBytesWritten += bytesWritten;
1482       }
1483     }
1484 
1485     if (CODEC_ID_VORBIS.equals(track.codecId)) {
1486       // Vorbis decoder in android MediaCodec [1] expects the last 4 bytes of the sample to be the
1487       // number of samples in the current page. This definition holds good only for Ogg and
1488       // irrelevant for Matroska. So we always set this to -1 (the decoder will ignore this value if
1489       // we set it to -1). The android platform media extractor [2] does the same.
1490       // [1] https://android.googlesource.com/platform/frameworks/av/+/lollipop-release/media/libstagefright/codecs/vorbis/dec/SoftVorbis.cpp#314
1491       // [2] https://android.googlesource.com/platform/frameworks/av/+/lollipop-release/media/libstagefright/NuMediaExtractor.cpp#474
1492       vorbisNumPageSamples.setPosition(0);
1493       output.sampleData(vorbisNumPageSamples, 4);
1494       sampleBytesWritten += 4;
1495     }
1496 
1497     return finishWriteSampleData();
1498   }
1499 
1500   /**
1501    * Called by {@link #writeSampleData(ExtractorInput, Track, int)} when the sample has been
1502    * written. Returns the final sample size and resets state for the next sample.
1503    */
finishWriteSampleData()1504   private int finishWriteSampleData() {
1505     int sampleSize = sampleBytesWritten;
1506     resetWriteSampleData();
1507     return sampleSize;
1508   }
1509 
1510   /** Resets state used by {@link #writeSampleData(ExtractorInput, Track, int)}. */
resetWriteSampleData()1511   private void resetWriteSampleData() {
1512     sampleBytesRead = 0;
1513     sampleBytesWritten = 0;
1514     sampleCurrentNalBytesRemaining = 0;
1515     sampleEncodingHandled = false;
1516     sampleSignalByteRead = false;
1517     samplePartitionCountRead = false;
1518     samplePartitionCount = 0;
1519     sampleSignalByte = (byte) 0;
1520     sampleInitializationVectorRead = false;
1521     sampleStrippedBytes.reset();
1522   }
1523 
writeSubtitleSampleData(ExtractorInput input, byte[] samplePrefix, int size)1524   private void writeSubtitleSampleData(ExtractorInput input, byte[] samplePrefix, int size)
1525       throws IOException {
1526     int sizeWithPrefix = samplePrefix.length + size;
1527     if (subtitleSample.capacity() < sizeWithPrefix) {
1528       // Initialize subripSample to contain the required prefix and have space to hold a subtitle
1529       // twice as long as this one.
1530       subtitleSample.data = Arrays.copyOf(samplePrefix, sizeWithPrefix + size);
1531     } else {
1532       System.arraycopy(samplePrefix, 0, subtitleSample.data, 0, samplePrefix.length);
1533     }
1534     input.readFully(subtitleSample.data, samplePrefix.length, size);
1535     subtitleSample.reset(sizeWithPrefix);
1536     // Defer writing the data to the track output. We need to modify the sample data by setting
1537     // the correct end timecode, which we might not have yet.
1538   }
1539 
1540   /**
1541    * Overwrites the end timecode in {@code subtitleData} with the correctly formatted time derived
1542    * from {@code durationUs}.
1543    *
1544    * <p>See documentation on {@link #SSA_DIALOGUE_FORMAT} and {@link #SUBRIP_PREFIX} for why we use
1545    * the duration as the end timecode.
1546    *
1547    * @param codecId The subtitle codec; must be {@link #CODEC_ID_SUBRIP} or {@link #CODEC_ID_ASS}.
1548    * @param durationUs The duration of the sample, in microseconds.
1549    * @param subtitleData The subtitle sample in which to overwrite the end timecode (output
1550    *     parameter).
1551    */
setSubtitleEndTime(String codecId, long durationUs, byte[] subtitleData)1552   private static void setSubtitleEndTime(String codecId, long durationUs, byte[] subtitleData) {
1553     byte[] endTimecode;
1554     int endTimecodeOffset;
1555     switch (codecId) {
1556       case CODEC_ID_SUBRIP:
1557         endTimecode =
1558             formatSubtitleTimecode(
1559                 durationUs, SUBRIP_TIMECODE_FORMAT, SUBRIP_TIMECODE_LAST_VALUE_SCALING_FACTOR);
1560         endTimecodeOffset = SUBRIP_PREFIX_END_TIMECODE_OFFSET;
1561         break;
1562       case CODEC_ID_ASS:
1563         endTimecode =
1564             formatSubtitleTimecode(
1565                 durationUs, SSA_TIMECODE_FORMAT, SSA_TIMECODE_LAST_VALUE_SCALING_FACTOR);
1566         endTimecodeOffset = SSA_PREFIX_END_TIMECODE_OFFSET;
1567         break;
1568       default:
1569         throw new IllegalArgumentException();
1570     }
1571     System.arraycopy(endTimecode, 0, subtitleData, endTimecodeOffset, endTimecode.length);
1572   }
1573 
1574   /**
1575    * Formats {@code timeUs} using {@code timecodeFormat}, and sets it as the end timecode in {@code
1576    * subtitleSampleData}.
1577    */
formatSubtitleTimecode( long timeUs, String timecodeFormat, long lastTimecodeValueScalingFactor)1578   private static byte[] formatSubtitleTimecode(
1579       long timeUs, String timecodeFormat, long lastTimecodeValueScalingFactor) {
1580     Assertions.checkArgument(timeUs != C.TIME_UNSET);
1581     byte[] timeCodeData;
1582     int hours = (int) (timeUs / (3600 * C.MICROS_PER_SECOND));
1583     timeUs -= (hours * 3600 * C.MICROS_PER_SECOND);
1584     int minutes = (int) (timeUs / (60 * C.MICROS_PER_SECOND));
1585     timeUs -= (minutes * 60 * C.MICROS_PER_SECOND);
1586     int seconds = (int) (timeUs / C.MICROS_PER_SECOND);
1587     timeUs -= (seconds * C.MICROS_PER_SECOND);
1588     int lastValue = (int) (timeUs / lastTimecodeValueScalingFactor);
1589     timeCodeData =
1590         Util.getUtf8Bytes(
1591             String.format(Locale.US, timecodeFormat, hours, minutes, seconds, lastValue));
1592     return timeCodeData;
1593   }
1594 
1595   /**
1596    * Writes {@code length} bytes of sample data into {@code target} at {@code offset}, consisting of
1597    * pending {@link #sampleStrippedBytes} and any remaining data read from {@code input}.
1598    */
writeToTarget(ExtractorInput input, byte[] target, int offset, int length)1599   private void writeToTarget(ExtractorInput input, byte[] target, int offset, int length)
1600       throws IOException {
1601     int pendingStrippedBytes = Math.min(length, sampleStrippedBytes.bytesLeft());
1602     input.readFully(target, offset + pendingStrippedBytes, length - pendingStrippedBytes);
1603     if (pendingStrippedBytes > 0) {
1604       sampleStrippedBytes.readBytes(target, offset, pendingStrippedBytes);
1605     }
1606   }
1607 
1608   /**
1609    * Outputs up to {@code length} bytes of sample data to {@code output}, consisting of either
1610    * {@link #sampleStrippedBytes} or data read from {@code input}.
1611    */
writeToOutput(ExtractorInput input, TrackOutput output, int length)1612   private int writeToOutput(ExtractorInput input, TrackOutput output, int length)
1613       throws IOException {
1614     int bytesWritten;
1615     int strippedBytesLeft = sampleStrippedBytes.bytesLeft();
1616     if (strippedBytesLeft > 0) {
1617       bytesWritten = Math.min(length, strippedBytesLeft);
1618       output.sampleData(sampleStrippedBytes, bytesWritten);
1619     } else {
1620       bytesWritten = output.sampleData(input, length, false);
1621     }
1622     return bytesWritten;
1623   }
1624 
1625   /**
1626    * Builds a {@link SeekMap} from the recently gathered Cues information.
1627    *
1628    * @return The built {@link SeekMap}. The returned {@link SeekMap} may be unseekable if cues
1629    *     information was missing or incomplete.
1630    */
buildSeekMap()1631   private SeekMap buildSeekMap() {
1632     if (segmentContentPosition == C.POSITION_UNSET || durationUs == C.TIME_UNSET
1633         || cueTimesUs == null || cueTimesUs.size() == 0
1634         || cueClusterPositions == null || cueClusterPositions.size() != cueTimesUs.size()) {
1635       // Cues information is missing or incomplete.
1636       cueTimesUs = null;
1637       cueClusterPositions = null;
1638       return new SeekMap.Unseekable(durationUs);
1639     }
1640     int cuePointsSize = cueTimesUs.size();
1641     int[] sizes = new int[cuePointsSize];
1642     long[] offsets = new long[cuePointsSize];
1643     long[] durationsUs = new long[cuePointsSize];
1644     long[] timesUs = new long[cuePointsSize];
1645     for (int i = 0; i < cuePointsSize; i++) {
1646       timesUs[i] = cueTimesUs.get(i);
1647       offsets[i] = segmentContentPosition + cueClusterPositions.get(i);
1648     }
1649     for (int i = 0; i < cuePointsSize - 1; i++) {
1650       sizes[i] = (int) (offsets[i + 1] - offsets[i]);
1651       durationsUs[i] = timesUs[i + 1] - timesUs[i];
1652     }
1653     sizes[cuePointsSize - 1] =
1654         (int) (segmentContentPosition + segmentContentSize - offsets[cuePointsSize - 1]);
1655     durationsUs[cuePointsSize - 1] = durationUs - timesUs[cuePointsSize - 1];
1656 
1657     long lastDurationUs = durationsUs[cuePointsSize - 1];
1658     if (lastDurationUs <= 0) {
1659       Log.w(TAG, "Discarding last cue point with unexpected duration: " + lastDurationUs);
1660       sizes = Arrays.copyOf(sizes, sizes.length - 1);
1661       offsets = Arrays.copyOf(offsets, offsets.length - 1);
1662       durationsUs = Arrays.copyOf(durationsUs, durationsUs.length - 1);
1663       timesUs = Arrays.copyOf(timesUs, timesUs.length - 1);
1664     }
1665 
1666     cueTimesUs = null;
1667     cueClusterPositions = null;
1668     return new ChunkIndex(sizes, offsets, durationsUs, timesUs);
1669   }
1670 
1671   /**
1672    * Updates the position of the holder to Cues element's position if the extractor configuration
1673    * permits use of master seek entry. After building Cues sets the holder's position back to where
1674    * it was before.
1675    *
1676    * @param seekPosition The holder whose position will be updated.
1677    * @param currentPosition Current position of the input.
1678    * @return Whether the seek position was updated.
1679    */
maybeSeekForCues(PositionHolder seekPosition, long currentPosition)1680   private boolean maybeSeekForCues(PositionHolder seekPosition, long currentPosition) {
1681     if (seekForCues) {
1682       seekPositionAfterBuildingCues = currentPosition;
1683       seekPosition.position = cuesContentPosition;
1684       seekForCues = false;
1685       return true;
1686     }
1687     // After parsing Cues, seek back to original position if available. We will not do this unless
1688     // we seeked to get to the Cues in the first place.
1689     if (sentSeekMap && seekPositionAfterBuildingCues != C.POSITION_UNSET) {
1690       seekPosition.position = seekPositionAfterBuildingCues;
1691       seekPositionAfterBuildingCues = C.POSITION_UNSET;
1692       return true;
1693     }
1694     return false;
1695   }
1696 
scaleTimecodeToUs(long unscaledTimecode)1697   private long scaleTimecodeToUs(long unscaledTimecode) throws ParserException {
1698     if (timecodeScale == C.TIME_UNSET) {
1699       throw new ParserException("Can't scale timecode prior to timecodeScale being set.");
1700     }
1701     return Util.scaleLargeTimestamp(unscaledTimecode, timecodeScale, 1000);
1702   }
1703 
isCodecSupported(String codecId)1704   private static boolean isCodecSupported(String codecId) {
1705     return CODEC_ID_VP8.equals(codecId)
1706         || CODEC_ID_VP9.equals(codecId)
1707         || CODEC_ID_AV1.equals(codecId)
1708         || CODEC_ID_MPEG2.equals(codecId)
1709         || CODEC_ID_MPEG4_SP.equals(codecId)
1710         || CODEC_ID_MPEG4_ASP.equals(codecId)
1711         || CODEC_ID_MPEG4_AP.equals(codecId)
1712         || CODEC_ID_H264.equals(codecId)
1713         || CODEC_ID_H265.equals(codecId)
1714         || CODEC_ID_FOURCC.equals(codecId)
1715         || CODEC_ID_THEORA.equals(codecId)
1716         || CODEC_ID_OPUS.equals(codecId)
1717         || CODEC_ID_VORBIS.equals(codecId)
1718         || CODEC_ID_AAC.equals(codecId)
1719         || CODEC_ID_MP2.equals(codecId)
1720         || CODEC_ID_MP3.equals(codecId)
1721         || CODEC_ID_AC3.equals(codecId)
1722         || CODEC_ID_E_AC3.equals(codecId)
1723         || CODEC_ID_TRUEHD.equals(codecId)
1724         || CODEC_ID_DTS.equals(codecId)
1725         || CODEC_ID_DTS_EXPRESS.equals(codecId)
1726         || CODEC_ID_DTS_LOSSLESS.equals(codecId)
1727         || CODEC_ID_FLAC.equals(codecId)
1728         || CODEC_ID_ACM.equals(codecId)
1729         || CODEC_ID_PCM_INT_LIT.equals(codecId)
1730         || CODEC_ID_SUBRIP.equals(codecId)
1731         || CODEC_ID_ASS.equals(codecId)
1732         || CODEC_ID_VOBSUB.equals(codecId)
1733         || CODEC_ID_PGS.equals(codecId)
1734         || CODEC_ID_DVBSUB.equals(codecId);
1735   }
1736 
1737   /**
1738    * Returns an array that can store (at least) {@code length} elements, which will be either a new
1739    * array or {@code array} if it's not null and large enough.
1740    */
ensureArrayCapacity(int[] array, int length)1741   private static int[] ensureArrayCapacity(int[] array, int length) {
1742     if (array == null) {
1743       return new int[length];
1744     } else if (array.length >= length) {
1745       return array;
1746     } else {
1747       // Double the size to avoid allocating constantly if the required length increases gradually.
1748       return new int[Math.max(array.length * 2, length)];
1749     }
1750   }
1751 
1752   /** Passes events through to the outer {@link MatroskaExtractor}. */
1753   private final class InnerEbmlProcessor implements EbmlProcessor {
1754 
1755     @Override
1756     @ElementType
getElementType(int id)1757     public int getElementType(int id) {
1758       return MatroskaExtractor.this.getElementType(id);
1759     }
1760 
1761     @Override
isLevel1Element(int id)1762     public boolean isLevel1Element(int id) {
1763       return MatroskaExtractor.this.isLevel1Element(id);
1764     }
1765 
1766     @Override
startMasterElement(int id, long contentPosition, long contentSize)1767     public void startMasterElement(int id, long contentPosition, long contentSize)
1768         throws ParserException {
1769       MatroskaExtractor.this.startMasterElement(id, contentPosition, contentSize);
1770     }
1771 
1772     @Override
endMasterElement(int id)1773     public void endMasterElement(int id) throws ParserException {
1774       MatroskaExtractor.this.endMasterElement(id);
1775     }
1776 
1777     @Override
integerElement(int id, long value)1778     public void integerElement(int id, long value) throws ParserException {
1779       MatroskaExtractor.this.integerElement(id, value);
1780     }
1781 
1782     @Override
floatElement(int id, double value)1783     public void floatElement(int id, double value) throws ParserException {
1784       MatroskaExtractor.this.floatElement(id, value);
1785     }
1786 
1787     @Override
stringElement(int id, String value)1788     public void stringElement(int id, String value) throws ParserException {
1789       MatroskaExtractor.this.stringElement(id, value);
1790     }
1791 
1792     @Override
binaryElement(int id, int contentsSize, ExtractorInput input)1793     public void binaryElement(int id, int contentsSize, ExtractorInput input) throws IOException {
1794       MatroskaExtractor.this.binaryElement(id, contentsSize, input);
1795     }
1796   }
1797 
1798   /**
1799    * Rechunks TrueHD sample data into groups of {@link Ac3Util#TRUEHD_RECHUNK_SAMPLE_COUNT} samples.
1800    */
1801   private static final class TrueHdSampleRechunker {
1802 
1803     private final byte[] syncframePrefix;
1804 
1805     private boolean foundSyncframe;
1806     private int chunkSampleCount;
1807     private long chunkTimeUs;
1808     private @C.BufferFlags int chunkFlags;
1809     private int chunkSize;
1810     private int chunkOffset;
1811 
TrueHdSampleRechunker()1812     public TrueHdSampleRechunker() {
1813       syncframePrefix = new byte[Ac3Util.TRUEHD_SYNCFRAME_PREFIX_LENGTH];
1814     }
1815 
reset()1816     public void reset() {
1817       foundSyncframe = false;
1818       chunkSampleCount = 0;
1819     }
1820 
startSample(ExtractorInput input)1821     public void startSample(ExtractorInput input) throws IOException {
1822       if (foundSyncframe) {
1823         return;
1824       }
1825       input.peekFully(syncframePrefix, 0, Ac3Util.TRUEHD_SYNCFRAME_PREFIX_LENGTH);
1826       input.resetPeekPosition();
1827       if (Ac3Util.parseTrueHdSyncframeAudioSampleCount(syncframePrefix) == 0) {
1828         return;
1829       }
1830       foundSyncframe = true;
1831     }
1832 
sampleMetadata( Track track, long timeUs, @C.BufferFlags int flags, int size, int offset)1833     public void sampleMetadata(
1834         Track track, long timeUs, @C.BufferFlags int flags, int size, int offset) {
1835       if (!foundSyncframe) {
1836         return;
1837       }
1838       if (chunkSampleCount++ == 0) {
1839         // This is the first sample in the chunk.
1840         chunkTimeUs = timeUs;
1841         chunkFlags = flags;
1842         chunkSize = 0;
1843       }
1844       chunkSize += size;
1845       chunkOffset = offset; // The offset is to the end of the sample.
1846       if (chunkSampleCount >= Ac3Util.TRUEHD_RECHUNK_SAMPLE_COUNT) {
1847         outputPendingSampleMetadata(track);
1848       }
1849     }
1850 
outputPendingSampleMetadata(Track track)1851     public void outputPendingSampleMetadata(Track track) {
1852       if (chunkSampleCount > 0) {
1853         track.output.sampleMetadata(
1854             chunkTimeUs, chunkFlags, chunkSize, chunkOffset, track.cryptoData);
1855         chunkSampleCount = 0;
1856       }
1857     }
1858   }
1859 
1860   private static final class Track {
1861 
1862     private static final int DISPLAY_UNIT_PIXELS = 0;
1863     private static final int MAX_CHROMATICITY = 50000;  // Defined in CTA-861.3.
1864     /**
1865      * Default max content light level (CLL) that should be encoded into hdrStaticInfo.
1866      */
1867     private static final int DEFAULT_MAX_CLL = 1000;  // nits.
1868 
1869     /**
1870      * Default frame-average light level (FALL) that should be encoded into hdrStaticInfo.
1871      */
1872     private static final int DEFAULT_MAX_FALL = 200;  // nits.
1873 
1874     // Common elements.
1875     public String name;
1876     public String codecId;
1877     public int number;
1878     public int type;
1879     public int defaultSampleDurationNs;
1880     public int maxBlockAdditionId;
1881     public boolean hasContentEncryption;
1882     public byte[] sampleStrippedBytes;
1883     public TrackOutput.CryptoData cryptoData;
1884     public byte[] codecPrivate;
1885     public DrmInitData drmInitData;
1886 
1887     // Video elements.
1888     public int width = Format.NO_VALUE;
1889     public int height = Format.NO_VALUE;
1890     public int displayWidth = Format.NO_VALUE;
1891     public int displayHeight = Format.NO_VALUE;
1892     public int displayUnit = DISPLAY_UNIT_PIXELS;
1893     @C.Projection public int projectionType = Format.NO_VALUE;
1894     public float projectionPoseYaw = 0f;
1895     public float projectionPosePitch = 0f;
1896     public float projectionPoseRoll = 0f;
1897     public byte[] projectionData = null;
1898     @C.StereoMode
1899     public int stereoMode = Format.NO_VALUE;
1900     public boolean hasColorInfo = false;
1901     @C.ColorSpace
1902     public int colorSpace = Format.NO_VALUE;
1903     @C.ColorTransfer
1904     public int colorTransfer = Format.NO_VALUE;
1905     @C.ColorRange
1906     public int colorRange = Format.NO_VALUE;
1907     public int maxContentLuminance = DEFAULT_MAX_CLL;
1908     public int maxFrameAverageLuminance = DEFAULT_MAX_FALL;
1909     public float primaryRChromaticityX = Format.NO_VALUE;
1910     public float primaryRChromaticityY = Format.NO_VALUE;
1911     public float primaryGChromaticityX = Format.NO_VALUE;
1912     public float primaryGChromaticityY = Format.NO_VALUE;
1913     public float primaryBChromaticityX = Format.NO_VALUE;
1914     public float primaryBChromaticityY = Format.NO_VALUE;
1915     public float whitePointChromaticityX = Format.NO_VALUE;
1916     public float whitePointChromaticityY = Format.NO_VALUE;
1917     public float maxMasteringLuminance = Format.NO_VALUE;
1918     public float minMasteringLuminance = Format.NO_VALUE;
1919 
1920     // Audio elements. Initially set to their default values.
1921     public int channelCount = 1;
1922     public int audioBitDepth = Format.NO_VALUE;
1923     public int sampleRate = 8000;
1924     public long codecDelayNs = 0;
1925     public long seekPreRollNs = 0;
1926     @Nullable public TrueHdSampleRechunker trueHdSampleRechunker;
1927 
1928     // Text elements.
1929     public boolean flagForced;
1930     public boolean flagDefault = true;
1931     private String language = "eng";
1932 
1933     // Set when the output is initialized. nalUnitLengthFieldLength is only set for H264/H265.
1934     public TrackOutput output;
1935     public int nalUnitLengthFieldLength;
1936 
1937     /** Initializes the track with an output. */
initializeOutput(ExtractorOutput output, int trackId)1938     public void initializeOutput(ExtractorOutput output, int trackId) throws ParserException {
1939       String mimeType;
1940       int maxInputSize = Format.NO_VALUE;
1941       @C.PcmEncoding int pcmEncoding = Format.NO_VALUE;
1942       @Nullable List<byte[]> initializationData = null;
1943       switch (codecId) {
1944         case CODEC_ID_VP8:
1945           mimeType = MimeTypes.VIDEO_VP8;
1946           break;
1947         case CODEC_ID_VP9:
1948           mimeType = MimeTypes.VIDEO_VP9;
1949           break;
1950         case CODEC_ID_AV1:
1951           mimeType = MimeTypes.VIDEO_AV1;
1952           break;
1953         case CODEC_ID_MPEG2:
1954           mimeType = MimeTypes.VIDEO_MPEG2;
1955           break;
1956         case CODEC_ID_MPEG4_SP:
1957         case CODEC_ID_MPEG4_ASP:
1958         case CODEC_ID_MPEG4_AP:
1959           mimeType = MimeTypes.VIDEO_MP4V;
1960           initializationData =
1961               codecPrivate == null ? null : Collections.singletonList(codecPrivate);
1962           break;
1963         case CODEC_ID_H264:
1964           mimeType = MimeTypes.VIDEO_H264;
1965           AvcConfig avcConfig = AvcConfig.parse(new ParsableByteArray(codecPrivate));
1966           initializationData = avcConfig.initializationData;
1967           nalUnitLengthFieldLength = avcConfig.nalUnitLengthFieldLength;
1968           break;
1969         case CODEC_ID_H265:
1970           mimeType = MimeTypes.VIDEO_H265;
1971           HevcConfig hevcConfig = HevcConfig.parse(new ParsableByteArray(codecPrivate));
1972           initializationData = hevcConfig.initializationData;
1973           nalUnitLengthFieldLength = hevcConfig.nalUnitLengthFieldLength;
1974           break;
1975         case CODEC_ID_FOURCC:
1976           Pair<String, @NullableType List<byte[]>> pair =
1977               parseFourCcPrivate(new ParsableByteArray(codecPrivate));
1978           mimeType = pair.first;
1979           initializationData = pair.second;
1980           break;
1981         case CODEC_ID_THEORA:
1982           // TODO: This can be set to the real mimeType if/when we work out what initializationData
1983           // should be set to for this case.
1984           mimeType = MimeTypes.VIDEO_UNKNOWN;
1985           break;
1986         case CODEC_ID_VORBIS:
1987           mimeType = MimeTypes.AUDIO_VORBIS;
1988           maxInputSize = VORBIS_MAX_INPUT_SIZE;
1989           initializationData = parseVorbisCodecPrivate(codecPrivate);
1990           break;
1991         case CODEC_ID_OPUS:
1992           mimeType = MimeTypes.AUDIO_OPUS;
1993           maxInputSize = OPUS_MAX_INPUT_SIZE;
1994           initializationData = new ArrayList<>(3);
1995           initializationData.add(codecPrivate);
1996           initializationData.add(
1997               ByteBuffer.allocate(8).order(ByteOrder.LITTLE_ENDIAN).putLong(codecDelayNs).array());
1998           initializationData.add(
1999               ByteBuffer.allocate(8).order(ByteOrder.LITTLE_ENDIAN).putLong(seekPreRollNs).array());
2000           break;
2001         case CODEC_ID_AAC:
2002           mimeType = MimeTypes.AUDIO_AAC;
2003           initializationData = Collections.singletonList(codecPrivate);
2004           break;
2005         case CODEC_ID_MP2:
2006           mimeType = MimeTypes.AUDIO_MPEG_L2;
2007           maxInputSize = MpegAudioUtil.MAX_FRAME_SIZE_BYTES;
2008           break;
2009         case CODEC_ID_MP3:
2010           mimeType = MimeTypes.AUDIO_MPEG;
2011           maxInputSize = MpegAudioUtil.MAX_FRAME_SIZE_BYTES;
2012           break;
2013         case CODEC_ID_AC3:
2014           mimeType = MimeTypes.AUDIO_AC3;
2015           break;
2016         case CODEC_ID_E_AC3:
2017           mimeType = MimeTypes.AUDIO_E_AC3;
2018           break;
2019         case CODEC_ID_TRUEHD:
2020           mimeType = MimeTypes.AUDIO_TRUEHD;
2021           trueHdSampleRechunker = new TrueHdSampleRechunker();
2022           break;
2023         case CODEC_ID_DTS:
2024         case CODEC_ID_DTS_EXPRESS:
2025           mimeType = MimeTypes.AUDIO_DTS;
2026           break;
2027         case CODEC_ID_DTS_LOSSLESS:
2028           mimeType = MimeTypes.AUDIO_DTS_HD;
2029           break;
2030         case CODEC_ID_FLAC:
2031           mimeType = MimeTypes.AUDIO_FLAC;
2032           initializationData = Collections.singletonList(codecPrivate);
2033           break;
2034         case CODEC_ID_ACM:
2035           mimeType = MimeTypes.AUDIO_RAW;
2036           if (parseMsAcmCodecPrivate(new ParsableByteArray(codecPrivate))) {
2037             pcmEncoding = Util.getPcmEncoding(audioBitDepth);
2038             if (pcmEncoding == C.ENCODING_INVALID) {
2039               pcmEncoding = Format.NO_VALUE;
2040               mimeType = MimeTypes.AUDIO_UNKNOWN;
2041               Log.w(TAG, "Unsupported PCM bit depth: " + audioBitDepth + ". Setting mimeType to "
2042                   + mimeType);
2043             }
2044           } else {
2045             mimeType = MimeTypes.AUDIO_UNKNOWN;
2046             Log.w(TAG, "Non-PCM MS/ACM is unsupported. Setting mimeType to " + mimeType);
2047           }
2048           break;
2049         case CODEC_ID_PCM_INT_LIT:
2050           mimeType = MimeTypes.AUDIO_RAW;
2051           pcmEncoding = Util.getPcmEncoding(audioBitDepth);
2052           if (pcmEncoding == C.ENCODING_INVALID) {
2053             pcmEncoding = Format.NO_VALUE;
2054             mimeType = MimeTypes.AUDIO_UNKNOWN;
2055             Log.w(TAG, "Unsupported PCM bit depth: " + audioBitDepth + ". Setting mimeType to "
2056                 + mimeType);
2057           }
2058           break;
2059         case CODEC_ID_SUBRIP:
2060           mimeType = MimeTypes.APPLICATION_SUBRIP;
2061           break;
2062         case CODEC_ID_ASS:
2063           mimeType = MimeTypes.TEXT_SSA;
2064           break;
2065         case CODEC_ID_VOBSUB:
2066           mimeType = MimeTypes.APPLICATION_VOBSUB;
2067           initializationData = Collections.singletonList(codecPrivate);
2068           break;
2069         case CODEC_ID_PGS:
2070           mimeType = MimeTypes.APPLICATION_PGS;
2071           break;
2072         case CODEC_ID_DVBSUB:
2073           mimeType = MimeTypes.APPLICATION_DVBSUBS;
2074           // Init data: composition_page (2), ancillary_page (2)
2075           initializationData = Collections.singletonList(new byte[] {codecPrivate[0],
2076               codecPrivate[1], codecPrivate[2], codecPrivate[3]});
2077           break;
2078         default:
2079           throw new ParserException("Unrecognized codec identifier.");
2080       }
2081 
2082       @C.SelectionFlags int selectionFlags = 0;
2083       selectionFlags |= flagDefault ? C.SELECTION_FLAG_DEFAULT : 0;
2084       selectionFlags |= flagForced ? C.SELECTION_FLAG_FORCED : 0;
2085 
2086       int type;
2087       Format.Builder formatBuilder = new Format.Builder();
2088       // TODO: Consider reading the name elements of the tracks and, if present, incorporating them
2089       // into the trackId passed when creating the formats.
2090       if (MimeTypes.isAudio(mimeType)) {
2091         type = C.TRACK_TYPE_AUDIO;
2092         formatBuilder
2093             .setChannelCount(channelCount)
2094             .setSampleRate(sampleRate)
2095             .setPcmEncoding(pcmEncoding);
2096       } else if (MimeTypes.isVideo(mimeType)) {
2097         type = C.TRACK_TYPE_VIDEO;
2098         if (displayUnit == Track.DISPLAY_UNIT_PIXELS) {
2099           displayWidth = displayWidth == Format.NO_VALUE ? width : displayWidth;
2100           displayHeight = displayHeight == Format.NO_VALUE ? height : displayHeight;
2101         }
2102         float pixelWidthHeightRatio = Format.NO_VALUE;
2103         if (displayWidth != Format.NO_VALUE && displayHeight != Format.NO_VALUE) {
2104           pixelWidthHeightRatio = ((float) (height * displayWidth)) / (width * displayHeight);
2105         }
2106         @Nullable ColorInfo colorInfo = null;
2107         if (hasColorInfo) {
2108           @Nullable byte[] hdrStaticInfo = getHdrStaticInfo();
2109           colorInfo = new ColorInfo(colorSpace, colorRange, colorTransfer, hdrStaticInfo);
2110         }
2111         int rotationDegrees = Format.NO_VALUE;
2112 
2113         if (TRACK_NAME_TO_ROTATION_DEGREES.containsKey(name)) {
2114           rotationDegrees = TRACK_NAME_TO_ROTATION_DEGREES.get(name);
2115         }
2116         if (projectionType == C.PROJECTION_RECTANGULAR
2117             && Float.compare(projectionPoseYaw, 0f) == 0
2118             && Float.compare(projectionPosePitch, 0f) == 0) {
2119           // The range of projectionPoseRoll is [-180, 180].
2120           if (Float.compare(projectionPoseRoll, 0f) == 0) {
2121             rotationDegrees = 0;
2122           } else if (Float.compare(projectionPosePitch, 90f) == 0) {
2123             rotationDegrees = 90;
2124           } else if (Float.compare(projectionPosePitch, -180f) == 0
2125               || Float.compare(projectionPosePitch, 180f) == 0) {
2126             rotationDegrees = 180;
2127           } else if (Float.compare(projectionPosePitch, -90f) == 0) {
2128             rotationDegrees = 270;
2129           }
2130         }
2131         formatBuilder
2132             .setWidth(width)
2133             .setHeight(height)
2134             .setPixelWidthHeightRatio(pixelWidthHeightRatio)
2135             .setRotationDegrees(rotationDegrees)
2136             .setProjectionData(projectionData)
2137             .setStereoMode(stereoMode)
2138             .setColorInfo(colorInfo);
2139       } else if (MimeTypes.APPLICATION_SUBRIP.equals(mimeType)) {
2140         type = C.TRACK_TYPE_TEXT;
2141       } else if (MimeTypes.TEXT_SSA.equals(mimeType)) {
2142         type = C.TRACK_TYPE_TEXT;
2143         initializationData = new ArrayList<>(2);
2144         initializationData.add(SSA_DIALOGUE_FORMAT);
2145         initializationData.add(codecPrivate);
2146       } else if (MimeTypes.APPLICATION_VOBSUB.equals(mimeType)
2147           || MimeTypes.APPLICATION_PGS.equals(mimeType)
2148           || MimeTypes.APPLICATION_DVBSUBS.equals(mimeType)) {
2149         type = C.TRACK_TYPE_TEXT;
2150       } else {
2151         throw new ParserException("Unexpected MIME type.");
2152       }
2153 
2154       if (!TRACK_NAME_TO_ROTATION_DEGREES.containsKey(name)) {
2155         formatBuilder.setLabel(name);
2156       }
2157 
2158       Format format =
2159           formatBuilder
2160               .setId(trackId)
2161               .setSampleMimeType(mimeType)
2162               .setMaxInputSize(maxInputSize)
2163               .setLanguage(language)
2164               .setSelectionFlags(selectionFlags)
2165               .setInitializationData(initializationData)
2166               .setDrmInitData(drmInitData)
2167               .build();
2168 
2169       this.output = output.track(number, type);
2170       this.output.format(format);
2171     }
2172 
2173     /** Forces any pending sample metadata to be flushed to the output. */
outputPendingSampleMetadata()2174     public void outputPendingSampleMetadata() {
2175       if (trueHdSampleRechunker != null) {
2176         trueHdSampleRechunker.outputPendingSampleMetadata(this);
2177       }
2178     }
2179 
2180     /** Resets any state stored in the track in response to a seek. */
reset()2181     public void reset() {
2182       if (trueHdSampleRechunker != null) {
2183         trueHdSampleRechunker.reset();
2184       }
2185     }
2186 
2187     /** Returns the HDR Static Info as defined in CTA-861.3. */
2188     @Nullable
getHdrStaticInfo()2189     private byte[] getHdrStaticInfo() {
2190       // Are all fields present.
2191       if (primaryRChromaticityX == Format.NO_VALUE || primaryRChromaticityY == Format.NO_VALUE
2192           || primaryGChromaticityX == Format.NO_VALUE || primaryGChromaticityY == Format.NO_VALUE
2193           || primaryBChromaticityX == Format.NO_VALUE || primaryBChromaticityY == Format.NO_VALUE
2194           || whitePointChromaticityX == Format.NO_VALUE
2195           || whitePointChromaticityY == Format.NO_VALUE || maxMasteringLuminance == Format.NO_VALUE
2196           || minMasteringLuminance == Format.NO_VALUE) {
2197         return null;
2198       }
2199 
2200       byte[] hdrStaticInfoData = new byte[25];
2201       ByteBuffer hdrStaticInfo = ByteBuffer.wrap(hdrStaticInfoData).order(ByteOrder.LITTLE_ENDIAN);
2202       hdrStaticInfo.put((byte) 0);  // Type.
2203       hdrStaticInfo.putShort((short) ((primaryRChromaticityX * MAX_CHROMATICITY) + 0.5f));
2204       hdrStaticInfo.putShort((short) ((primaryRChromaticityY * MAX_CHROMATICITY) + 0.5f));
2205       hdrStaticInfo.putShort((short) ((primaryGChromaticityX * MAX_CHROMATICITY)  + 0.5f));
2206       hdrStaticInfo.putShort((short) ((primaryGChromaticityY * MAX_CHROMATICITY) + 0.5f));
2207       hdrStaticInfo.putShort((short) ((primaryBChromaticityX * MAX_CHROMATICITY) + 0.5f));
2208       hdrStaticInfo.putShort((short) ((primaryBChromaticityY * MAX_CHROMATICITY) + 0.5f));
2209       hdrStaticInfo.putShort((short) ((whitePointChromaticityX * MAX_CHROMATICITY) + 0.5f));
2210       hdrStaticInfo.putShort((short) ((whitePointChromaticityY * MAX_CHROMATICITY) + 0.5f));
2211       hdrStaticInfo.putShort((short) (maxMasteringLuminance + 0.5f));
2212       hdrStaticInfo.putShort((short) (minMasteringLuminance + 0.5f));
2213       hdrStaticInfo.putShort((short) maxContentLuminance);
2214       hdrStaticInfo.putShort((short) maxFrameAverageLuminance);
2215       return hdrStaticInfoData;
2216     }
2217 
2218     /**
2219      * Builds initialization data for a {@link Format} from FourCC codec private data.
2220      *
2221      * @return The codec mime type and initialization data. If the compression type is not supported
2222      *     then the mime type is set to {@link MimeTypes#VIDEO_UNKNOWN} and the initialization data
2223      *     is {@code null}.
2224      * @throws ParserException If the initialization data could not be built.
2225      */
parseFourCcPrivate( ParsableByteArray buffer)2226     private static Pair<String, @NullableType List<byte[]>> parseFourCcPrivate(
2227         ParsableByteArray buffer) throws ParserException {
2228       try {
2229         buffer.skipBytes(16); // size(4), width(4), height(4), planes(2), bitcount(2).
2230         long compression = buffer.readLittleEndianUnsignedInt();
2231         if (compression == FOURCC_COMPRESSION_DIVX) {
2232           return new Pair<>(MimeTypes.VIDEO_DIVX, null);
2233         } else if (compression == FOURCC_COMPRESSION_H263) {
2234           return new Pair<>(MimeTypes.VIDEO_H263, null);
2235         } else if (compression == FOURCC_COMPRESSION_VC1) {
2236           // Search for the initialization data from the end of the BITMAPINFOHEADER. The last 20
2237           // bytes of which are: sizeImage(4), xPel/m (4), yPel/m (4), clrUsed(4), clrImportant(4).
2238           int startOffset = buffer.getPosition() + 20;
2239           byte[] bufferData = buffer.data;
2240           for (int offset = startOffset; offset < bufferData.length - 4; offset++) {
2241             if (bufferData[offset] == 0x00
2242                 && bufferData[offset + 1] == 0x00
2243                 && bufferData[offset + 2] == 0x01
2244                 && bufferData[offset + 3] == 0x0F) {
2245               // We've found the initialization data.
2246               byte[] initializationData = Arrays.copyOfRange(bufferData, offset, bufferData.length);
2247               return new Pair<>(MimeTypes.VIDEO_VC1, Collections.singletonList(initializationData));
2248             }
2249           }
2250           throw new ParserException("Failed to find FourCC VC1 initialization data");
2251         }
2252       } catch (ArrayIndexOutOfBoundsException e) {
2253         throw new ParserException("Error parsing FourCC private data");
2254       }
2255 
2256       Log.w(TAG, "Unknown FourCC. Setting mimeType to " + MimeTypes.VIDEO_UNKNOWN);
2257       return new Pair<>(MimeTypes.VIDEO_UNKNOWN, null);
2258     }
2259 
2260     /**
2261      * Builds initialization data for a {@link Format} from Vorbis codec private data.
2262      *
2263      * @return The initialization data for the {@link Format}.
2264      * @throws ParserException If the initialization data could not be built.
2265      */
parseVorbisCodecPrivate(byte[] codecPrivate)2266     private static List<byte[]> parseVorbisCodecPrivate(byte[] codecPrivate)
2267         throws ParserException {
2268       try {
2269         if (codecPrivate[0] != 0x02) {
2270           throw new ParserException("Error parsing vorbis codec private");
2271         }
2272         int offset = 1;
2273         int vorbisInfoLength = 0;
2274         while (codecPrivate[offset] == (byte) 0xFF) {
2275           vorbisInfoLength += 0xFF;
2276           offset++;
2277         }
2278         vorbisInfoLength += codecPrivate[offset++];
2279 
2280         int vorbisSkipLength = 0;
2281         while (codecPrivate[offset] == (byte) 0xFF) {
2282           vorbisSkipLength += 0xFF;
2283           offset++;
2284         }
2285         vorbisSkipLength += codecPrivate[offset++];
2286 
2287         if (codecPrivate[offset] != 0x01) {
2288           throw new ParserException("Error parsing vorbis codec private");
2289         }
2290         byte[] vorbisInfo = new byte[vorbisInfoLength];
2291         System.arraycopy(codecPrivate, offset, vorbisInfo, 0, vorbisInfoLength);
2292         offset += vorbisInfoLength;
2293         if (codecPrivate[offset] != 0x03) {
2294           throw new ParserException("Error parsing vorbis codec private");
2295         }
2296         offset += vorbisSkipLength;
2297         if (codecPrivate[offset] != 0x05) {
2298           throw new ParserException("Error parsing vorbis codec private");
2299         }
2300         byte[] vorbisBooks = new byte[codecPrivate.length - offset];
2301         System.arraycopy(codecPrivate, offset, vorbisBooks, 0, codecPrivate.length - offset);
2302         List<byte[]> initializationData = new ArrayList<>(2);
2303         initializationData.add(vorbisInfo);
2304         initializationData.add(vorbisBooks);
2305         return initializationData;
2306       } catch (ArrayIndexOutOfBoundsException e) {
2307         throw new ParserException("Error parsing vorbis codec private");
2308       }
2309     }
2310 
2311     /**
2312      * Parses an MS/ACM codec private, returning whether it indicates PCM audio.
2313      *
2314      * @return Whether the codec private indicates PCM audio.
2315      * @throws ParserException If a parsing error occurs.
2316      */
parseMsAcmCodecPrivate(ParsableByteArray buffer)2317     private static boolean parseMsAcmCodecPrivate(ParsableByteArray buffer) throws ParserException {
2318       try {
2319         int formatTag = buffer.readLittleEndianUnsignedShort();
2320         if (formatTag == WAVE_FORMAT_PCM) {
2321           return true;
2322         } else if (formatTag == WAVE_FORMAT_EXTENSIBLE) {
2323           buffer.setPosition(WAVE_FORMAT_SIZE + 6); // unionSamples(2), channelMask(4)
2324           return buffer.readLong() == WAVE_SUBFORMAT_PCM.getMostSignificantBits()
2325               && buffer.readLong() == WAVE_SUBFORMAT_PCM.getLeastSignificantBits();
2326         } else {
2327           return false;
2328         }
2329       } catch (ArrayIndexOutOfBoundsException e) {
2330         throw new ParserException("Error parsing MS/ACM codec private");
2331       }
2332     }
2333   }
2334 }
2335