Home | History | Annotate | Download | only in boxes
      1 package com.coremedia.iso.boxes;
      2 
      3 import com.coremedia.iso.IsoTypeReader;
      4 import com.coremedia.iso.IsoTypeWriter;
      5 import com.googlecode.mp4parser.AbstractFullBox;
      6 
      7 import java.nio.ByteBuffer;
      8 import java.util.ArrayList;
      9 import java.util.Collections;
     10 import java.util.List;
     11 
     12 import static com.googlecode.mp4parser.util.CastUtils.l2i;
     13 
     14 /**
     15  * <pre>
     16  * aligned(8) class CompositionOffsetBox
     17  * extends FullBox(ctts, version = 0, 0) {
     18  *  unsigned int(32) entry_count;
     19  *  int i;
     20  *  if (version==0) {
     21  *   for (i=0; i < entry_count; i++) {
     22  *    unsigned int(32) sample_count;
     23  *    unsigned int(32) sample_offset;
     24  *   }
     25  *  }
     26  *  else if (version == 1) {
     27  *   for (i=0; i < entry_count; i++) {
     28  *    unsigned int(32) sample_count;
     29  *    signed int(32) sample_offset;
     30  *   }
     31  *  }
     32  * }
     33  * </pre>
     34  * <p/>
     35  * This box provides the offset between decoding time and composition time.
     36  * In version 0 of this box the decoding time must be less than the composition time, and
     37  * the offsets are expressed as unsigned numbers such that
     38  * CT(n) = DT(n) + CTTS(n) where CTTS(n) is the (uncompressed) table entry for sample n.
     39  * <p/>
     40  * In version 1 of this box, the composition timeline and the decoding timeline are
     41  * still derived from each other, but the offsets are signed.
     42  * It is recommended that for the computed composition timestamps, there is
     43  * exactly one with the value 0 (zero).
     44  */
     45 public class CompositionTimeToSample extends AbstractFullBox {
     46     public static final String TYPE = "ctts";
     47 
     48     List<Entry> entries = Collections.emptyList();
     49 
     50     public CompositionTimeToSample() {
     51         super(TYPE);
     52     }
     53 
     54     protected long getContentSize() {
     55         return 8 + 8 * entries.size();
     56     }
     57 
     58     public List<Entry> getEntries() {
     59         return entries;
     60     }
     61 
     62     public void setEntries(List<Entry> entries) {
     63         this.entries = entries;
     64     }
     65 
     66     @Override
     67     public void _parseDetails(ByteBuffer content) {
     68         parseVersionAndFlags(content);
     69         int numberOfEntries = l2i(IsoTypeReader.readUInt32(content));
     70         entries = new ArrayList<Entry>(numberOfEntries);
     71         for (int i = 0; i < numberOfEntries; i++) {
     72             Entry e = new Entry(l2i(IsoTypeReader.readUInt32(content)), content.getInt());
     73             entries.add(e);
     74         }
     75     }
     76 
     77     @Override
     78     protected void getContent(ByteBuffer byteBuffer) {
     79         writeVersionAndFlags(byteBuffer);
     80         IsoTypeWriter.writeUInt32(byteBuffer, entries.size());
     81 
     82         for (Entry entry : entries) {
     83             IsoTypeWriter.writeUInt32(byteBuffer, entry.getCount());
     84             byteBuffer.putInt(entry.getOffset());
     85         }
     86 
     87     }
     88 
     89 
     90     public static class Entry {
     91         int count;
     92         int offset;
     93 
     94         public Entry(int count, int offset) {
     95             this.count = count;
     96             this.offset = offset;
     97         }
     98 
     99         public int getCount() {
    100             return count;
    101         }
    102 
    103         public int getOffset() {
    104             return offset;
    105         }
    106 
    107         public void setCount(int count) {
    108             this.count = count;
    109         }
    110 
    111         public void setOffset(int offset) {
    112             this.offset = offset;
    113         }
    114 
    115         @Override
    116         public String toString() {
    117             return "Entry{" +
    118                     "count=" + count +
    119                     ", offset=" + offset +
    120                     '}';
    121         }
    122     }
    123 
    124 
    125     /**
    126      * Decompresses the list of entries and returns the list of composition times.
    127      *
    128      * @return decoding time per sample
    129      */
    130     public static int[] blowupCompositionTimes(List<CompositionTimeToSample.Entry> entries) {
    131         long numOfSamples = 0;
    132         for (CompositionTimeToSample.Entry entry : entries) {
    133             numOfSamples += entry.getCount();
    134         }
    135         assert numOfSamples <= Integer.MAX_VALUE;
    136         int[] decodingTime = new int[(int) numOfSamples];
    137 
    138         int current = 0;
    139 
    140 
    141         for (CompositionTimeToSample.Entry entry : entries) {
    142             for (int i = 0; i < entry.getCount(); i++) {
    143                 decodingTime[current++] = entry.getOffset();
    144             }
    145         }
    146 
    147         return decodingTime;
    148     }
    149 
    150 }
    151