Home | History | Annotate | Download | only in boxes
      1 /*
      2  * Copyright 2008 CoreMedia AG, Hamburg
      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;
     18 
     19 
     20 import com.coremedia.iso.IsoTypeReader;
     21 import com.coremedia.iso.IsoTypeWriter;
     22 import com.googlecode.mp4parser.AbstractFullBox;
     23 
     24 import java.io.IOException;
     25 import java.nio.ByteBuffer;
     26 import java.util.LinkedList;
     27 import java.util.List;
     28 
     29 import static com.googlecode.mp4parser.util.CastUtils.l2i;
     30 
     31 /**
     32  * <code>
     33  * Box Type  : 'elst'<br>
     34  * Container: {@link EditBox}('edts')<br>
     35  * Mandatory: No<br>
     36  * Quantity  : Zero or one</code><br><br>
     37  * This box contains an explicit timeline map. Each entry defines part of the track time-line: by mapping part of
     38  * the media time-line, or by indicating 'empty' time, or by defining a 'dwell', where a single time-point in the
     39  * media is held for a period.<br>
     40  * Note that edits are not restricted to fall on sample times. This means that when entering an edit, it can be
     41  * necessary to (a) back up to a sync point, and pre-roll from there and then (b) be careful about the duration of
     42  * the first sample - it might have been truncated if the edit enters it during its normal duration. If this is audio,
     43  * that frame might need to be decoded, and then the final slicing done. Likewise, the duration of the last sample
     44  * in an edit might need slicing. <br>
     45  * Starting offsets for tracks (streams) are represented by an initial empty edit. For example, to play a track from
     46  * its start for 30 seconds, but at 10 seconds into the presentation, we have the following edit list:<br>
     47  * <p/>
     48  * <li>Entry-count = 2</li>
     49  * <li>Segment-duration = 10 seconds</li>
     50  * <li>Media-Time = -1</li>
     51  * <li>Media-Rate = 1</li>
     52  * <li>Segment-duration = 30 seconds (could be the length of the whole track)</li>
     53  * <li>Media-Time = 0 seconds</li>
     54  * <li>Media-Rate = 1</li>
     55  */
     56 public class EditListBox extends AbstractFullBox {
     57     private List<Entry> entries = new LinkedList<Entry>();
     58     public static final String TYPE = "elst";
     59 
     60     public EditListBox() {
     61         super(TYPE);
     62     }
     63 
     64 
     65     public List<Entry> getEntries() {
     66         return entries;
     67     }
     68 
     69     public void setEntries(List<Entry> entries) {
     70         this.entries = entries;
     71     }
     72 
     73     protected long getContentSize() {
     74         long contentSize = 8;
     75         if (getVersion() == 1) {
     76             contentSize += entries.size() * 20;
     77         } else {
     78             contentSize += entries.size() * 12;
     79         }
     80 
     81         return contentSize;
     82     }
     83 
     84     @Override
     85     public void _parseDetails(ByteBuffer content) {
     86         parseVersionAndFlags(content);
     87         int entryCount = l2i(IsoTypeReader.readUInt32(content));
     88         entries = new LinkedList<Entry>();
     89         for (int i = 0; i < entryCount; i++) {
     90             entries.add(new Entry(this, content));
     91 
     92         }
     93     }
     94 
     95     @Override
     96     protected void getContent(ByteBuffer byteBuffer) {
     97         writeVersionAndFlags(byteBuffer);
     98         IsoTypeWriter.writeUInt32(byteBuffer, entries.size());
     99         for (Entry entry : entries) {
    100             entry.getContent(byteBuffer);
    101         }
    102     }
    103 
    104     @Override
    105     public String toString() {
    106         return "EditListBox{" +
    107                 "entries=" + entries +
    108                 '}';
    109     }
    110 
    111     public static class Entry {
    112         private long segmentDuration;
    113         private long mediaTime;
    114         private double mediaRate;
    115         EditListBox editListBox;
    116 
    117         /**
    118          * Creates a new <code>Entry</code> with all values set.
    119          *
    120          * @param segmentDuration duration in movie timescale
    121          * @param mediaTime       starting time
    122          * @param mediaRate       relative play rate
    123          */
    124         public Entry(EditListBox editListBox, long segmentDuration, long mediaTime, double mediaRate) {
    125             this.segmentDuration = segmentDuration;
    126             this.mediaTime = mediaTime;
    127             this.mediaRate = mediaRate;
    128             this.editListBox = editListBox;
    129         }
    130 
    131         public Entry(EditListBox editListBox, ByteBuffer bb) {
    132             if (editListBox.getVersion() == 1) {
    133                 segmentDuration = IsoTypeReader.readUInt64(bb);
    134                 mediaTime = IsoTypeReader.readUInt64(bb);
    135                 mediaRate = IsoTypeReader.readFixedPoint1616(bb);
    136             } else {
    137                 segmentDuration = IsoTypeReader.readUInt32(bb);
    138                 mediaTime = IsoTypeReader.readUInt32(bb);
    139                 mediaRate = IsoTypeReader.readFixedPoint1616(bb);
    140             }
    141             this.editListBox = editListBox;
    142         }
    143 
    144         /**
    145          * The segment duration is an integer that specifies the duration
    146          * of this edit segment in units of the timescale in the Movie
    147          * Header Box
    148          *
    149          * @return segment duration in movie timescale
    150          */
    151         public long getSegmentDuration() {
    152             return segmentDuration;
    153         }
    154 
    155         /**
    156          * The segment duration is an integer that specifies the duration
    157          * of this edit segment in units of the timescale in the Movie
    158          * Header Box
    159          *
    160          * @param segmentDuration new segment duration in movie timescale
    161          */
    162         public void setSegmentDuration(long segmentDuration) {
    163             this.segmentDuration = segmentDuration;
    164         }
    165 
    166         /**
    167          * The media time is an integer containing the starting time
    168          * within the media of a specific edit segment(in media time
    169          * scale units, in composition time)
    170          *
    171          * @return starting time
    172          */
    173         public long getMediaTime() {
    174             return mediaTime;
    175         }
    176 
    177         /**
    178          * The media time is an integer containing the starting time
    179          * within the media of a specific edit segment(in media time
    180          * scale units, in composition time)
    181          *
    182          * @param mediaTime starting time
    183          */
    184         public void setMediaTime(long mediaTime) {
    185             this.mediaTime = mediaTime;
    186         }
    187 
    188         /**
    189          * The media rate specifies the relative rate at which to play the
    190          * media corresponding to a specific edit segment.
    191          *
    192          * @return relative play rate
    193          */
    194         public double getMediaRate() {
    195             return mediaRate;
    196         }
    197 
    198         /**
    199          * The media rate specifies the relative rate at which to play the
    200          * media corresponding to a specific edit segment.
    201          *
    202          * @param mediaRate new relative play rate
    203          */
    204         public void setMediaRate(double mediaRate) {
    205             this.mediaRate = mediaRate;
    206         }
    207 
    208         @Override
    209         public boolean equals(Object o) {
    210             if (this == o) return true;
    211             if (o == null || getClass() != o.getClass()) return false;
    212 
    213             Entry entry = (Entry) o;
    214 
    215             if (mediaTime != entry.mediaTime) return false;
    216             if (segmentDuration != entry.segmentDuration) return false;
    217 
    218             return true;
    219         }
    220 
    221         @Override
    222         public int hashCode() {
    223             int result = (int) (segmentDuration ^ (segmentDuration >>> 32));
    224             result = 31 * result + (int) (mediaTime ^ (mediaTime >>> 32));
    225             return result;
    226         }
    227 
    228         public void getContent(ByteBuffer bb)  {
    229             if (editListBox.getVersion() == 1) {
    230                 IsoTypeWriter.writeUInt64(bb, segmentDuration);
    231                 IsoTypeWriter.writeUInt64(bb, mediaTime);
    232             } else {
    233                 IsoTypeWriter.writeUInt32(bb, l2i(segmentDuration));
    234                 bb.putInt(l2i(mediaTime));
    235             }
    236             IsoTypeWriter.writeFixedPont1616(bb, mediaRate);
    237         }
    238 
    239         @Override
    240         public String toString() {
    241             return "Entry{" +
    242                     "segmentDuration=" + segmentDuration +
    243                     ", mediaTime=" + mediaTime +
    244                     ", mediaRate=" + mediaRate +
    245                     '}';
    246         }
    247     }
    248 }
    249