1 /* 2 * Copyright 2009 castLabs GmbH, Berlin 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 17 package com.coremedia.iso.boxes.fragment; 18 19 import com.coremedia.iso.IsoTypeReader; 20 import com.coremedia.iso.IsoTypeWriter; 21 import com.coremedia.iso.boxes.MovieBox; 22 import com.googlecode.mp4parser.AbstractFullBox; 23 24 import java.nio.ByteBuffer; 25 import java.util.ArrayList; 26 import java.util.List; 27 28 import static com.googlecode.mp4parser.util.CastUtils.l2i; 29 30 /** 31 * aligned(8) class TrackRunBox 32 * extends FullBox('trun', 0, tr_flags) { 33 * unsigned int(32) sample_count; 34 * // the following are optional fields 35 * signed int(32) data_offset; 36 * unsigned int(32) first_sample_flags; 37 * // all fields in the following array are optional 38 * { 39 * unsigned int(32) sample_duration; 40 * unsigned int(32) sample_size; 41 * unsigned int(32) sample_flags 42 * unsigned int(32) sample_composition_time_offset; 43 * }[ sample_count ] 44 * } 45 */ 46 47 public class TrackRunBox extends AbstractFullBox { 48 public static final String TYPE = "trun"; 49 private int dataOffset; 50 private SampleFlags firstSampleFlags; 51 private List<Entry> entries = new ArrayList<Entry>(); 52 53 getEntries()54 public List<Entry> getEntries() { 55 return entries; 56 } 57 58 public static class Entry { 59 private long sampleDuration; 60 private long sampleSize; 61 private SampleFlags sampleFlags; 62 private int sampleCompositionTimeOffset; 63 Entry()64 public Entry() { 65 } 66 Entry(long sampleDuration, long sampleSize, SampleFlags sampleFlags, int sampleCompositionTimeOffset)67 public Entry(long sampleDuration, long sampleSize, SampleFlags sampleFlags, int sampleCompositionTimeOffset) { 68 this.sampleDuration = sampleDuration; 69 this.sampleSize = sampleSize; 70 this.sampleFlags = sampleFlags; 71 this.sampleCompositionTimeOffset = sampleCompositionTimeOffset; 72 } 73 getSampleDuration()74 public long getSampleDuration() { 75 return sampleDuration; 76 } 77 getSampleSize()78 public long getSampleSize() { 79 return sampleSize; 80 } 81 getSampleFlags()82 public SampleFlags getSampleFlags() { 83 return sampleFlags; 84 } 85 getSampleCompositionTimeOffset()86 public int getSampleCompositionTimeOffset() { 87 return sampleCompositionTimeOffset; 88 } 89 setSampleDuration(long sampleDuration)90 public void setSampleDuration(long sampleDuration) { 91 this.sampleDuration = sampleDuration; 92 } 93 setSampleSize(long sampleSize)94 public void setSampleSize(long sampleSize) { 95 this.sampleSize = sampleSize; 96 } 97 setSampleFlags(SampleFlags sampleFlags)98 public void setSampleFlags(SampleFlags sampleFlags) { 99 this.sampleFlags = sampleFlags; 100 } 101 setSampleCompositionTimeOffset(int sampleCompositionTimeOffset)102 public void setSampleCompositionTimeOffset(int sampleCompositionTimeOffset) { 103 this.sampleCompositionTimeOffset = sampleCompositionTimeOffset; 104 } 105 106 @Override toString()107 public String toString() { 108 return "Entry{" + 109 "sampleDuration=" + sampleDuration + 110 ", sampleSize=" + sampleSize + 111 ", sampleFlags=" + sampleFlags + 112 ", sampleCompositionTimeOffset=" + sampleCompositionTimeOffset + 113 '}'; 114 } 115 } 116 setDataOffset(int dataOffset)117 public void setDataOffset(int dataOffset) { 118 if (dataOffset == -1) { 119 setFlags(getFlags() & (0xFFFFFF ^ 1)); 120 } else { 121 setFlags(getFlags() | 0x1); // turn on dataoffset 122 } 123 this.dataOffset = dataOffset; 124 } 125 getSampleCompositionTimeOffsets()126 public long[] getSampleCompositionTimeOffsets() { 127 if (isSampleCompositionTimeOffsetPresent()) { 128 long[] result = new long[entries.size()]; 129 130 for (int i = 0; i < result.length; i++) { 131 result[i] = entries.get(i).getSampleCompositionTimeOffset(); 132 } 133 return result; 134 } 135 return null; 136 } 137 getTrackExtendsBox()138 public TrackExtendsBox getTrackExtendsBox() { 139 final TrackFragmentHeaderBox tfhd = ((TrackFragmentBox) getParent()).getTrackFragmentHeaderBox(); 140 final List<MovieBox> movieBoxes = tfhd.getIsoFile().getBoxes(MovieBox.class); 141 if (movieBoxes.size() == 0) { 142 return null; 143 } 144 145 final List<TrackExtendsBox> trexBoxes = movieBoxes.get(0).getBoxes(TrackExtendsBox.class, true); 146 TrackExtendsBox trex = null; 147 for (TrackExtendsBox aTrex : trexBoxes) { 148 if (aTrex.getTrackId() == tfhd.getTrackId()) { 149 trex = aTrex; 150 } 151 } 152 return trex; 153 } 154 TrackRunBox()155 public TrackRunBox() { 156 super(TYPE); 157 } 158 getContentSize()159 protected long getContentSize() { 160 long size = 8; 161 int flags = getFlags(); 162 163 if ((flags & 0x1) == 0x1) { //dataOffsetPresent 164 size += 4; 165 } 166 if ((flags & 0x4) == 0x4) { //firstSampleFlagsPresent 167 size += 4; 168 } 169 170 long entrySize = 0; 171 if ((flags & 0x100) == 0x100) { //sampleDurationPresent 172 entrySize += 4; 173 } 174 if ((flags & 0x200) == 0x200) { //sampleSizePresent 175 entrySize += 4; 176 } 177 if ((flags & 0x400) == 0x400) { //sampleFlagsPresent 178 entrySize += 4; 179 } 180 if ((flags & 0x800) == 0x800) { //sampleCompositionTimeOffsetPresent 181 entrySize += 4; 182 } 183 size += entrySize * entries.size(); 184 return size; 185 } 186 getContent(ByteBuffer byteBuffer)187 protected void getContent(ByteBuffer byteBuffer) { 188 writeVersionAndFlags(byteBuffer); 189 IsoTypeWriter.writeUInt32(byteBuffer, entries.size()); 190 int flags = getFlags(); 191 192 if ((flags & 0x1) == 1) { //dataOffsetPresent 193 IsoTypeWriter.writeUInt32(byteBuffer, dataOffset); 194 } 195 if ((flags & 0x4) == 0x4) { //firstSampleFlagsPresent 196 firstSampleFlags.getContent(byteBuffer); 197 } 198 199 for (Entry entry : entries) { 200 if ((flags & 0x100) == 0x100) { //sampleDurationPresent 201 IsoTypeWriter.writeUInt32(byteBuffer, entry.sampleDuration); 202 } 203 if ((flags & 0x200) == 0x200) { //sampleSizePresent 204 IsoTypeWriter.writeUInt32(byteBuffer, entry.sampleSize); 205 } 206 if ((flags & 0x400) == 0x400) { //sampleFlagsPresent 207 entry.sampleFlags.getContent(byteBuffer); 208 } 209 if ((flags & 0x800) == 0x800) { //sampleCompositionTimeOffsetPresent 210 byteBuffer.putInt(entry.sampleCompositionTimeOffset); 211 } 212 } 213 } 214 215 @Override _parseDetails(ByteBuffer content)216 public void _parseDetails(ByteBuffer content) { 217 parseVersionAndFlags(content); 218 long sampleCount = IsoTypeReader.readUInt32(content); 219 220 if ((getFlags() & 0x1) == 1) { //dataOffsetPresent 221 dataOffset = l2i(IsoTypeReader.readUInt32(content)); 222 } else { 223 dataOffset = -1; 224 } 225 if ((getFlags() & 0x4) == 0x4) { //firstSampleFlagsPresent 226 firstSampleFlags = new SampleFlags(content); 227 } 228 229 for (int i = 0; i < sampleCount; i++) { 230 Entry entry = new Entry(); 231 if ((getFlags() & 0x100) == 0x100) { //sampleDurationPresent 232 entry.sampleDuration = IsoTypeReader.readUInt32(content); 233 } 234 if ((getFlags() & 0x200) == 0x200) { //sampleSizePresent 235 entry.sampleSize = IsoTypeReader.readUInt32(content); 236 } 237 if ((getFlags() & 0x400) == 0x400) { //sampleFlagsPresent 238 entry.sampleFlags = new SampleFlags(content); 239 } 240 if ((getFlags() & 0x800) == 0x800) { //sampleCompositionTimeOffsetPresent 241 entry.sampleCompositionTimeOffset = content.getInt(); 242 } 243 entries.add(entry); 244 } 245 246 } 247 getSampleCount()248 public long getSampleCount() { 249 return entries.size(); 250 } 251 isDataOffsetPresent()252 public boolean isDataOffsetPresent() { 253 return (getFlags() & 0x1) == 1; 254 } 255 isFirstSampleFlagsPresent()256 public boolean isFirstSampleFlagsPresent() { 257 return (getFlags() & 0x4) == 0x4; 258 } 259 260 isSampleSizePresent()261 public boolean isSampleSizePresent() { 262 return (getFlags() & 0x200) == 0x200; 263 } 264 isSampleDurationPresent()265 public boolean isSampleDurationPresent() { 266 return (getFlags() & 0x100) == 0x100; 267 } 268 isSampleFlagsPresent()269 public boolean isSampleFlagsPresent() { 270 return (getFlags() & 0x400) == 0x400; 271 } 272 isSampleCompositionTimeOffsetPresent()273 public boolean isSampleCompositionTimeOffsetPresent() { 274 return (getFlags() & 0x800) == 0x800; 275 } 276 setDataOffsetPresent(boolean v)277 public void setDataOffsetPresent(boolean v) { 278 if (v) { 279 setFlags(getFlags() | 0x01); 280 } else { 281 setFlags(getFlags() & (0xFFFFFF ^ 0x1)); 282 } 283 } 284 setSampleSizePresent(boolean v)285 public void setSampleSizePresent(boolean v) { 286 if (v) { 287 setFlags(getFlags() | 0x200); 288 } else { 289 setFlags(getFlags() & (0xFFFFFF ^ 0x200)); 290 } 291 } 292 setSampleDurationPresent(boolean v)293 public void setSampleDurationPresent(boolean v) { 294 295 if (v) { 296 setFlags(getFlags() | 0x100); 297 } else { 298 setFlags(getFlags() & (0xFFFFFF ^ 0x100)); 299 } 300 } 301 setSampleFlagsPresent(boolean v)302 public void setSampleFlagsPresent(boolean v) { 303 if (v) { 304 setFlags(getFlags() | 0x400); 305 } else { 306 setFlags(getFlags() & (0xFFFFFF ^ 0x400)); 307 } 308 } 309 setSampleCompositionTimeOffsetPresent(boolean v)310 public void setSampleCompositionTimeOffsetPresent(boolean v) { 311 if (v) { 312 setFlags(getFlags() | 0x800); 313 } else { 314 setFlags(getFlags() & (0xFFFFFF ^ 0x800)); 315 } 316 317 } 318 getDataOffset()319 public int getDataOffset() { 320 return dataOffset; 321 } 322 getFirstSampleFlags()323 public SampleFlags getFirstSampleFlags() { 324 return firstSampleFlags; 325 } 326 setFirstSampleFlags(SampleFlags firstSampleFlags)327 public void setFirstSampleFlags(SampleFlags firstSampleFlags) { 328 if (firstSampleFlags == null) { 329 setFlags(getFlags() & (0xFFFFFF ^ 0x4)); 330 } else { 331 setFlags(getFlags() | 0x4); 332 } 333 this.firstSampleFlags = firstSampleFlags; 334 } 335 336 @Override toString()337 public String toString() { 338 final StringBuilder sb = new StringBuilder(); 339 sb.append("TrackRunBox"); 340 sb.append("{sampleCount=").append(entries.size()); 341 sb.append(", dataOffset=").append(dataOffset); 342 sb.append(", dataOffsetPresent=").append(isDataOffsetPresent()); 343 sb.append(", sampleSizePresent=").append(isSampleSizePresent()); 344 sb.append(", sampleDurationPresent=").append(isSampleDurationPresent()); 345 sb.append(", sampleFlagsPresentPresent=").append(isSampleFlagsPresent()); 346 sb.append(", sampleCompositionTimeOffsetPresent=").append(isSampleCompositionTimeOffsetPresent()); 347 sb.append(", firstSampleFlags=").append(firstSampleFlags); 348 sb.append('}'); 349 return sb.toString(); 350 } 351 setEntries(List<Entry> entries)352 public void setEntries(List<Entry> entries) { 353 this.entries = entries; 354 } 355 } 356