package mp4.util.atom; /** * The decoding time-to-sample atom stores duration info for a media's * samples providing a mapping from the time in a media to a data sample. * * The atom contains a table of sample entries. Each entry contains a * sample count and sample duration. The delta's are computed as: * DT(n+1) = DT(n) + STTS(n) where STTS(n) is the uncompressed table * entry for sample n * The sum of all the deltas gives the length of the media in the track. * The edit list atom provides the initial CT value if it is non-empty. */ public class SttsAtom extends TimeToSampleAtom { /** * Constructor for the time-to-sample atom */ public SttsAtom() { super(new byte[]{'s','t','t','s'}); } /** * Copy constructor * @param old the atom to copy */ public SttsAtom(TimeToSampleAtom old) { super(old); } /** * Return the sample duration for the specified table index * @param index the stts table index * @return the sample duration for the index entry */ public long getSampleDuration(int index) { return getSampleValue(index); } /** * Set the sample duration for the specified table index. * @param index the stts table index * @param duration the new sample duration */ public void setSampleDuration(int index, long duration) { setSampleValue(index, duration); } /** * Cut the atom at the specified sample. This method creates a new * stts atom with the new data. This method searches through the table * looking for the appropriate sample. Once found a new table entry * needs to be created, but the subsequent entries remain the same. * Any preceding entry is ignored. * * @param sampleNum the sample where the atom should be cut * @return a new stts atom with the new data */ public SttsAtom cut(long sampleNum) { SttsAtom cutStts = new SttsAtom(); super.cut(sampleNum, cutStts); return cutStts; } @Override public void accept(AtomVisitor v) throws AtomException { v.visit(this); } long lowerBoundTime = 0; long lowerBoundSample = 1; int lowerBoundPos = 0; /** * Convert the sample number to a time, in media time-scale. * @param sampleNum the sample number * @return the time for the sample, in media time scale. */ public long sampleToTime(long sampleNum) { if (lowerBoundSample > sampleNum) { lowerBoundTime = 0; lowerBoundSample = 1; lowerBoundPos = 0; } long numEntries = getNumEntries(); for (int i = lowerBoundPos; i < numEntries; i++ ) { long count = getSampleCount(i); long duration = getSampleDuration(i); if ((sampleNum - lowerBoundSample) < count) { return ((sampleNum - lowerBoundSample) * duration) + lowerBoundTime; } lowerBoundPos = i+1; lowerBoundTime += count * duration; lowerBoundSample += count; } return 0; } public long getTotalSampleCount() { long numEntries = getNumEntries(); long sampleCnt = 0; for (int i = 0; i < numEntries; i++ ) { long count = getSampleCount(i); sampleCnt += count; } return sampleCnt; } /** * Given a time in the media return the data sample. * @param time the media time value * @return the sample number for the specified time */ public long timeToSample(long time) { long entries = getNumEntries(); long lowerBoundTime = 0; long lowerBoundSample = 1; for (int i = 0; i < entries; i++) { long count = getSampleCount(i); long duration = getSampleDuration(i); if ((time - lowerBoundTime) < (count * duration)) { return ((time - lowerBoundTime) / duration) + lowerBoundSample; } lowerBoundTime += count * duration; lowerBoundSample += count; } return 1; } }