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.mp4; 17 18 import androidx.annotation.Nullable; 19 import com.google.android.exoplayer2.extractor.ExtractorInput; 20 import com.google.android.exoplayer2.util.ParsableByteArray; 21 import java.io.IOException; 22 import org.checkerframework.checker.nullness.qual.MonotonicNonNull; 23 24 /** 25 * A holder for information corresponding to a single fragment of an mp4 file. 26 */ 27 /* package */ final class TrackFragment { 28 29 /** The default values for samples from the track fragment header. */ 30 public @MonotonicNonNull DefaultSampleValues header; 31 /** 32 * The position (byte offset) of the start of fragment. 33 */ 34 public long atomPosition; 35 /** 36 * The position (byte offset) of the start of data contained in the fragment. 37 */ 38 public long dataPosition; 39 /** 40 * The position (byte offset) of the start of auxiliary data. 41 */ 42 public long auxiliaryDataPosition; 43 /** 44 * The number of track runs of the fragment. 45 */ 46 public int trunCount; 47 /** 48 * The total number of samples in the fragment. 49 */ 50 public int sampleCount; 51 /** 52 * The position (byte offset) of the start of sample data of each track run in the fragment. 53 */ 54 public long[] trunDataPosition; 55 /** 56 * The number of samples contained by each track run in the fragment. 57 */ 58 public int[] trunLength; 59 /** 60 * The size of each sample in the fragment. 61 */ 62 public int[] sampleSizeTable; 63 /** The composition time offset of each sample in the fragment, in microseconds. */ 64 public int[] sampleCompositionTimeOffsetUsTable; 65 /** The decoding time of each sample in the fragment, in microseconds. */ 66 public long[] sampleDecodingTimeUsTable; 67 /** 68 * Indicates which samples are sync frames. 69 */ 70 public boolean[] sampleIsSyncFrameTable; 71 /** 72 * Whether the fragment defines encryption data. 73 */ 74 public boolean definesEncryptionData; 75 /** 76 * If {@link #definesEncryptionData} is true, indicates which samples use sub-sample encryption. 77 * Undefined otherwise. 78 */ 79 public boolean[] sampleHasSubsampleEncryptionTable; 80 /** Fragment specific track encryption. May be null. */ 81 @Nullable public TrackEncryptionBox trackEncryptionBox; 82 /** 83 * If {@link #definesEncryptionData} is true, contains binary sample encryption data. Undefined 84 * otherwise. 85 */ 86 public final ParsableByteArray sampleEncryptionData; 87 /** 88 * Whether {@link #sampleEncryptionData} needs populating with the actual encryption data. 89 */ 90 public boolean sampleEncryptionDataNeedsFill; 91 /** 92 * The absolute decode time of the start of the next fragment. 93 */ 94 public long nextFragmentDecodeTime; 95 TrackFragment()96 public TrackFragment() { 97 trunDataPosition = new long[0]; 98 trunLength = new int[0]; 99 sampleSizeTable = new int[0]; 100 sampleCompositionTimeOffsetUsTable = new int[0]; 101 sampleDecodingTimeUsTable = new long[0]; 102 sampleIsSyncFrameTable = new boolean[0]; 103 sampleHasSubsampleEncryptionTable = new boolean[0]; 104 sampleEncryptionData = new ParsableByteArray(); 105 } 106 107 /** 108 * Resets the fragment. 109 * <p> 110 * {@link #sampleCount} and {@link #nextFragmentDecodeTime} are set to 0, and both 111 * {@link #definesEncryptionData} and {@link #sampleEncryptionDataNeedsFill} is set to false, 112 * and {@link #trackEncryptionBox} is set to null. 113 */ reset()114 public void reset() { 115 trunCount = 0; 116 nextFragmentDecodeTime = 0; 117 definesEncryptionData = false; 118 sampleEncryptionDataNeedsFill = false; 119 trackEncryptionBox = null; 120 } 121 122 /** 123 * Configures the fragment for the specified number of samples. 124 * <p> 125 * The {@link #sampleCount} of the fragment is set to the specified sample count, and the 126 * contained tables are resized if necessary such that they are at least this length. 127 * 128 * @param sampleCount The number of samples in the new run. 129 */ initTables(int trunCount, int sampleCount)130 public void initTables(int trunCount, int sampleCount) { 131 this.trunCount = trunCount; 132 this.sampleCount = sampleCount; 133 if (trunLength.length < trunCount) { 134 trunDataPosition = new long[trunCount]; 135 trunLength = new int[trunCount]; 136 } 137 if (sampleSizeTable.length < sampleCount) { 138 // Size the tables 25% larger than needed, so as to make future resize operations less 139 // likely. The choice of 25% is relatively arbitrary. 140 int tableSize = (sampleCount * 125) / 100; 141 sampleSizeTable = new int[tableSize]; 142 sampleCompositionTimeOffsetUsTable = new int[tableSize]; 143 sampleDecodingTimeUsTable = new long[tableSize]; 144 sampleIsSyncFrameTable = new boolean[tableSize]; 145 sampleHasSubsampleEncryptionTable = new boolean[tableSize]; 146 } 147 } 148 149 /** 150 * Configures the fragment to be one that defines encryption data of the specified length. 151 * 152 * <p>{@link #definesEncryptionData} is set to true, and the {@link ParsableByteArray#limit() 153 * limit} of {@link #sampleEncryptionData} is set to the specified length. 154 * 155 * @param length The length in bytes of the encryption data. 156 */ initEncryptionData(int length)157 public void initEncryptionData(int length) { 158 sampleEncryptionData.reset(length); 159 definesEncryptionData = true; 160 sampleEncryptionDataNeedsFill = true; 161 } 162 163 /** 164 * Fills {@link #sampleEncryptionData} from the provided input. 165 * 166 * @param input An {@link ExtractorInput} from which to read the encryption data. 167 */ fillEncryptionData(ExtractorInput input)168 public void fillEncryptionData(ExtractorInput input) throws IOException { 169 input.readFully(sampleEncryptionData.data, 0, sampleEncryptionData.limit()); 170 sampleEncryptionData.setPosition(0); 171 sampleEncryptionDataNeedsFill = false; 172 } 173 174 /** 175 * Fills {@link #sampleEncryptionData} from the provided source. 176 * 177 * @param source A source from which to read the encryption data. 178 */ fillEncryptionData(ParsableByteArray source)179 public void fillEncryptionData(ParsableByteArray source) { 180 source.readBytes(sampleEncryptionData.data, 0, sampleEncryptionData.limit()); 181 sampleEncryptionData.setPosition(0); 182 sampleEncryptionDataNeedsFill = false; 183 } 184 185 /** 186 * Returns the sample presentation timestamp in microseconds. 187 * 188 * @param index The sample index. 189 * @return The presentation timestamps of this sample in microseconds. 190 */ getSamplePresentationTimeUs(int index)191 public long getSamplePresentationTimeUs(int index) { 192 return sampleDecodingTimeUsTable[index] + sampleCompositionTimeOffsetUsTable[index]; 193 } 194 195 /** Returns whether the sample at the given index has a subsample encryption table. */ sampleHasSubsampleEncryptionTable(int index)196 public boolean sampleHasSubsampleEncryptionTable(int index) { 197 return definesEncryptionData && sampleHasSubsampleEncryptionTable[index]; 198 } 199 } 200