Home | History | Annotate | Download | only in fragment
      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 
     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 
     64         public Entry() {
     65         }
     66 
     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 
     74         public long getSampleDuration() {
     75             return sampleDuration;
     76         }
     77 
     78         public long getSampleSize() {
     79             return sampleSize;
     80         }
     81 
     82         public SampleFlags getSampleFlags() {
     83             return sampleFlags;
     84         }
     85 
     86         public int getSampleCompositionTimeOffset() {
     87             return sampleCompositionTimeOffset;
     88         }
     89 
     90         public void setSampleDuration(long sampleDuration) {
     91             this.sampleDuration = sampleDuration;
     92         }
     93 
     94         public void setSampleSize(long sampleSize) {
     95             this.sampleSize = sampleSize;
     96         }
     97 
     98         public void setSampleFlags(SampleFlags sampleFlags) {
     99             this.sampleFlags = sampleFlags;
    100         }
    101 
    102         public void setSampleCompositionTimeOffset(int sampleCompositionTimeOffset) {
    103             this.sampleCompositionTimeOffset = sampleCompositionTimeOffset;
    104         }
    105 
    106         @Override
    107         public String toString() {
    108             return "Entry{" +
    109                     "sampleDuration=" + sampleDuration +
    110                     ", sampleSize=" + sampleSize +
    111                     ", sampleFlags=" + sampleFlags +
    112                     ", sampleCompositionTimeOffset=" + sampleCompositionTimeOffset +
    113                     '}';
    114         }
    115     }
    116 
    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 
    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 
    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 
    155     public TrackRunBox() {
    156         super(TYPE);
    157     }
    158 
    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 
    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
    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 
    248     public long getSampleCount() {
    249         return entries.size();
    250     }
    251 
    252     public boolean isDataOffsetPresent() {
    253         return (getFlags() & 0x1) == 1;
    254     }
    255 
    256     public boolean isFirstSampleFlagsPresent() {
    257         return (getFlags() & 0x4) == 0x4;
    258     }
    259 
    260 
    261     public boolean isSampleSizePresent() {
    262         return (getFlags() & 0x200) == 0x200;
    263     }
    264 
    265     public boolean isSampleDurationPresent() {
    266         return (getFlags() & 0x100) == 0x100;
    267     }
    268 
    269     public boolean isSampleFlagsPresent() {
    270         return (getFlags() & 0x400) == 0x400;
    271     }
    272 
    273     public boolean isSampleCompositionTimeOffsetPresent() {
    274         return (getFlags() & 0x800) == 0x800;
    275     }
    276 
    277     public void setDataOffsetPresent(boolean v) {
    278         if (v) {
    279             setFlags(getFlags() | 0x01);
    280         } else {
    281             setFlags(getFlags() & (0xFFFFFF ^ 0x1));
    282         }
    283     }
    284 
    285     public void setSampleSizePresent(boolean v) {
    286         if (v) {
    287             setFlags(getFlags() | 0x200);
    288         } else {
    289             setFlags(getFlags() & (0xFFFFFF ^ 0x200));
    290         }
    291     }
    292 
    293     public void setSampleDurationPresent(boolean v) {
    294 
    295         if (v) {
    296             setFlags(getFlags() | 0x100);
    297         } else {
    298             setFlags(getFlags() & (0xFFFFFF ^ 0x100));
    299         }
    300     }
    301 
    302     public void setSampleFlagsPresent(boolean v) {
    303         if (v) {
    304             setFlags(getFlags() | 0x400);
    305         } else {
    306             setFlags(getFlags() & (0xFFFFFF ^ 0x400));
    307         }
    308     }
    309 
    310     public void setSampleCompositionTimeOffsetPresent(boolean v) {
    311         if (v) {
    312             setFlags(getFlags() | 0x800);
    313         } else {
    314             setFlags(getFlags() & (0xFFFFFF ^ 0x800));
    315         }
    316 
    317     }
    318 
    319     public int getDataOffset() {
    320         return dataOffset;
    321     }
    322 
    323     public SampleFlags getFirstSampleFlags() {
    324         return firstSampleFlags;
    325     }
    326 
    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
    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 
    352     public void setEntries(List<Entry> entries) {
    353         this.entries = entries;
    354     }
    355 }
    356