package org.jcodec.containers.mp4.boxes; import java.lang.IllegalArgumentException; import java.lang.IllegalStateException; import java.nio.ByteBuffer; import java.util.List; //@formatter:off /** * This class is part of JCodec ( www.jcodec.org ) This software is distributed * under FreeBSD License * * Track fragment run * * To crate new box: * * <pre> * * Box box = TrunBox * .create(2) * .dataOffset(20) * .sampleCompositionOffset(new int[] { 11, 12 }) * .sampleDuration(new int[] { 15, 16 }) * .sampleFlags(new int[] { 100, 200 }) * .sampleSize(new int[] { 30, 40 }) * .create(); * * </pre> * * @author The JCodec project * */ //@formatter:on public class TrunBox extends FullBox { // @formatter:off private static final int DATA_OFFSET_AVAILABLE = 0x000001; private static final int FIRST_SAMPLE_FLAGS_AVAILABLE = 0x000004; private static final int SAMPLE_DURATION_AVAILABLE = 0x000100; private static final int SAMPLE_SIZE_AVAILABLE = 0x000200; private static final int SAMPLE_FLAGS_AVAILABLE = 0x000400; private static final int SAMPLE_COMPOSITION_OFFSET_AVAILABLE = 0x000800; // @formatter:on private int sampleCount; private int dataOffset; private int firstSampleFlags; private int[] sampleDuration; private int[] sampleSize; private int[] sampleFlags; private int[] sampleCompositionOffset; public static String fourcc() { return "trun"; } public void setDataOffset(int dataOffset) { this.dataOffset = dataOffset; } public static Factory create(int sampleCount) { return new Factory(TrunBox.createTrunBox1(sampleCount)); } public static Factory copy(TrunBox other) { TrunBox box = TrunBox .createTrunBox2(other.sampleCount, other.dataOffset, other.firstSampleFlags, other.sampleDuration, other.sampleSize, other.sampleFlags, other.sampleCompositionOffset); box.setFlags(other.getFlags()); box.setVersion(other.getVersion()); return new Factory(box); } public TrunBox(Header header) { super(header); } public static TrunBox createTrunBox1(int sampleCount) { TrunBox trun = new TrunBox(new Header(fourcc())); trun.sampleCount = sampleCount; return trun; } public static TrunBox createTrunBox2(int sampleCount, int dataOffset, int firstSampleFlags, int[] sampleDuration, int[] sampleSize, int[] sampleFlags, int[] sampleCompositionOffset) { TrunBox trun = new TrunBox(new Header(fourcc())); trun.sampleCount = sampleCount; trun.dataOffset = dataOffset; trun.firstSampleFlags = firstSampleFlags; trun.sampleDuration = sampleDuration; trun.sampleSize = sampleSize; trun.sampleFlags = sampleFlags; trun.sampleCompositionOffset = sampleCompositionOffset; return trun; } public static class Factory { private TrunBox box; protected Factory(TrunBox box) { this.box = box; } public Factory dataOffset(long dataOffset) { box.flags |= DATA_OFFSET_AVAILABLE; box.dataOffset = (int) dataOffset; return this; } public Factory firstSampleFlags(int firstSampleFlags) { if (box.isSampleFlagsAvailable()) throw new IllegalStateException("Sample flags already set on this object"); box.flags |= FIRST_SAMPLE_FLAGS_AVAILABLE; box.firstSampleFlags = firstSampleFlags; return this; } public Factory sampleDuration(int[] sampleDuration) { if (sampleDuration.length != box.sampleCount) throw new IllegalArgumentException("Argument array length not equal to sampleCount"); box.flags |= SAMPLE_DURATION_AVAILABLE; box.sampleDuration = sampleDuration; return this; } public Factory sampleSize(int[] sampleSize) { if (sampleSize.length != box.sampleCount) throw new IllegalArgumentException("Argument array length not equal to sampleCount"); box.flags |= SAMPLE_SIZE_AVAILABLE; box.sampleSize = sampleSize; return this; } public Factory sampleFlags(int[] sampleFlags) { if (sampleFlags.length != box.sampleCount) throw new IllegalArgumentException("Argument array length not equal to sampleCount"); if (box.isFirstSampleFlagsAvailable()) throw new IllegalStateException("First sample flags already set on this object"); box.flags |= SAMPLE_FLAGS_AVAILABLE; box.sampleFlags = sampleFlags; return this; } public Factory sampleCompositionOffset(int[] sampleCompositionOffset) { if (sampleCompositionOffset.length != box.sampleCount) throw new IllegalArgumentException("Argument array length not equal to sampleCount"); box.flags |= SAMPLE_COMPOSITION_OFFSET_AVAILABLE; box.sampleCompositionOffset = sampleCompositionOffset; return this; } public TrunBox create() { try { return box; } finally { box = null; } } } public long getSampleCount() { return sampleCount & 0xffffffffL; } public int getDataOffset() { return dataOffset; } public int getFirstSampleFlags() { return firstSampleFlags; } public int[] getSampleDurations() { return sampleDuration; } public int[] getSampleSizes() { return sampleSize; } public int[] getSamplesFlags() { return sampleFlags; } public int[] getSampleCompositionOffsets() { return sampleCompositionOffset; } public long getSampleDuration(int i) { return sampleDuration[i] & 0xffffffffL; } public long getSampleSize(int i) { return sampleSize[i] & 0xffffffffL; } public int getSampleFlags(int i) { return sampleFlags[i]; } public long getSampleCompositionOffset(int i) { return sampleCompositionOffset[i] & 0xffffffffL; } public boolean isDataOffsetAvailable() { return (flags & DATA_OFFSET_AVAILABLE) != 0; } public boolean isSampleCompositionOffsetAvailable() { return (flags & SAMPLE_COMPOSITION_OFFSET_AVAILABLE) != 0; } public boolean isSampleFlagsAvailable() { return (flags & SAMPLE_FLAGS_AVAILABLE) != 0; } public boolean isSampleSizeAvailable() { return (flags & SAMPLE_SIZE_AVAILABLE) != 0; } public boolean isSampleDurationAvailable() { return (flags & SAMPLE_DURATION_AVAILABLE) != 0; } public boolean isFirstSampleFlagsAvailable() { return (flags & FIRST_SAMPLE_FLAGS_AVAILABLE) != 0; } public static int flagsGetSampleDependsOn(int flags) { return (flags >> 6) & 0x3; } public static int flagsGetSampleIsDependedOn(int flags) { return (flags >> 8) & 0x3; } public static int flagsGetSampleHasRedundancy(int flags) { return (flags >> 10) & 0x3; } public static int flagsGetSamplePaddingValue(int flags) { return (flags >> 12) & 0x7; } public static int flagsGetSampleIsDifferentSample(int flags) { return (flags >> 15) & 0x1; } public static int flagsGetSampleDegradationPriority(int flags) { return (flags >> 16) & 0xffff; } public static TrunBox createTrunBox() { return new TrunBox(new Header(fourcc())); } @Override public void parse(ByteBuffer input) { super.parse(input); if (isSampleFlagsAvailable() && isFirstSampleFlagsAvailable()) throw new RuntimeException("Broken stream"); sampleCount = input.getInt(); if (isDataOffsetAvailable()) dataOffset = input.getInt(); if (isFirstSampleFlagsAvailable()) firstSampleFlags = input.getInt(); if (isSampleDurationAvailable()) sampleDuration = new int[sampleCount]; if (isSampleSizeAvailable()) sampleSize = new int[sampleCount]; if (isSampleFlagsAvailable()) sampleFlags = new int[sampleCount]; if (isSampleCompositionOffsetAvailable()) sampleCompositionOffset = new int[sampleCount]; for (int i = 0; i < sampleCount; i++) { if (isSampleDurationAvailable()) sampleDuration[i] = input.getInt(); if (isSampleSizeAvailable()) sampleSize[i] = input.getInt(); if (isSampleFlagsAvailable()) sampleFlags[i] = input.getInt(); if (isSampleCompositionOffsetAvailable()) sampleCompositionOffset[i] = input.getInt(); } } @Override public void doWrite(ByteBuffer out) { super.doWrite(out); out.putInt(sampleCount); if (isDataOffsetAvailable()) out.putInt(dataOffset); if (isFirstSampleFlagsAvailable()) out.putInt(firstSampleFlags); for (int i = 0; i < sampleCount; i++) { if (isSampleDurationAvailable()) out.putInt(sampleDuration[i]); if (isSampleSizeAvailable()) out.putInt(sampleSize[i]); if (isSampleFlagsAvailable()) out.putInt(sampleFlags[i]); if (isSampleCompositionOffsetAvailable()) out.putInt(sampleCompositionOffset[i]); } } protected void getModelFields(List<String> model) { model.add("sampleCount"); if (isDataOffsetAvailable()) model.add("dataOffset"); if (isFirstSampleFlagsAvailable()) model.add("firstSampleFlags"); if (isSampleDurationAvailable()) model.add("sampleDuration"); if (isSampleSizeAvailable()) model.add("sampleSize"); if (isSampleFlagsAvailable()) model.add("sampleFlags"); if (isSampleCompositionOffsetAvailable()) model.add("sampleCompositionOffset"); } }