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