package com.coremedia.iso.boxes; import com.coremedia.iso.BoxParser; import com.coremedia.iso.IsoBufferWrapper; import com.coremedia.iso.IsoFile; import com.coremedia.iso.IsoOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.List; /** * This box provides the offset between decoding time and composition time. * Since decoding time must be less than the composition time, the offsets * are expressed as unsigned numbers such that CT(n) = DT(n) + CTTS(n) where * CTTS(n) is the (uncompressed) table entry for sample n. The composition * time to sample table is optional and must only be present if DT and CT * differ for any samples. Hint tracks do not use this box. */ public class CompositionTimeToSample extends AbstractFullBox { public static final String TYPE = "ctts"; List<Entry> entries = Collections.emptyList(); public CompositionTimeToSample() { super(IsoFile.fourCCtoBytes(TYPE)); } protected long getContentSize() { return 4 + 8 * entries.size(); } public List<Entry> getEntries() { return entries; } public void setEntries(List<Entry> entries) { this.entries = entries; } @Override public void parse(IsoBufferWrapper in, long size, BoxParser boxParser, Box lastMovieFragmentBox) throws IOException { super.parse(in, size, boxParser, lastMovieFragmentBox); long numberOfEntries = in.readUInt32(); assert numberOfEntries <= Integer.MAX_VALUE : "Too many entries"; entries = new ArrayList<Entry>((int) numberOfEntries); for (int i = 0; i < numberOfEntries; i++) { Entry e = new Entry(in.readUInt32(), in.readInt32()); entries.add(e); } } protected void getContent(IsoOutputStream os) throws IOException { os.writeUInt32(entries.size()); for (Entry entry : entries) { os.writeUInt32(entry.getCount()); os.writeInt32(entry.getOffset()); } } public static class Entry { long count; int offset; public Entry(long count, int offset) { this.count = count; this.offset = offset; } public long getCount() { return count; } public int getOffset() { return offset; } public void setCount(long count) { this.count = count; } public void setOffset(int offset) { this.offset = offset; } @Override public String toString() { return "Entry{" + "count=" + count + ", offset=" + offset + '}'; } } /** * Decompresses the list of entries and returns the list of composition times. * * @return decoding time per sample */ public static int[] blowupCompositionTimes(List<CompositionTimeToSample.Entry> entries) { long numOfSamples = 0; for (CompositionTimeToSample.Entry entry : entries) { numOfSamples += entry.getCount(); } assert numOfSamples <= Integer.MAX_VALUE; int[] decodingTime = new int[(int) numOfSamples]; int current = 0; for (CompositionTimeToSample.Entry entry : entries) { for (int i = 0; i < entry.getCount(); i++) { decodingTime[current++] = entry.getOffset(); } } return decodingTime; } }