1/*
2 * Copyright 2012 Sebastian Annies, 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 */
16package com.googlecode.mp4parser.authoring;
17
18import com.coremedia.iso.boxes.*;
19import com.coremedia.iso.boxes.fragment.*;
20import com.coremedia.iso.boxes.mdat.SampleList;
21
22import java.nio.ByteBuffer;
23import java.util.Iterator;
24import java.util.LinkedList;
25import java.util.List;
26
27import static com.googlecode.mp4parser.util.CastUtils.l2i;
28
29/**
30 * Represents a single track of an MP4 file.
31 */
32public class Mp4TrackImpl extends AbstractTrack {
33    private List<ByteBuffer> samples;
34    private SampleDescriptionBox sampleDescriptionBox;
35    private List<TimeToSampleBox.Entry> decodingTimeEntries;
36    private List<CompositionTimeToSample.Entry> compositionTimeEntries;
37    private long[] syncSamples = new long[0];
38    private List<SampleDependencyTypeBox.Entry> sampleDependencies;
39    private TrackMetaData trackMetaData = new TrackMetaData();
40    private String handler;
41    private AbstractMediaHeaderBox mihd;
42
43    public Mp4TrackImpl(TrackBox trackBox) {
44        final long trackId = trackBox.getTrackHeaderBox().getTrackId();
45        samples = new SampleList(trackBox);
46        SampleTableBox stbl = trackBox.getMediaBox().getMediaInformationBox().getSampleTableBox();
47        handler = trackBox.getMediaBox().getHandlerBox().getHandlerType();
48
49        mihd = trackBox.getMediaBox().getMediaInformationBox().getMediaHeaderBox();
50        decodingTimeEntries = new LinkedList<TimeToSampleBox.Entry>();
51        compositionTimeEntries = new LinkedList<CompositionTimeToSample.Entry>();
52        sampleDependencies = new LinkedList<SampleDependencyTypeBox.Entry>();
53
54        decodingTimeEntries.addAll(stbl.getTimeToSampleBox().getEntries());
55        if (stbl.getCompositionTimeToSample() != null) {
56            compositionTimeEntries.addAll(stbl.getCompositionTimeToSample().getEntries());
57        }
58        if (stbl.getSampleDependencyTypeBox() != null) {
59            sampleDependencies.addAll(stbl.getSampleDependencyTypeBox().getEntries());
60        }
61        if (stbl.getSyncSampleBox() != null) {
62            syncSamples = stbl.getSyncSampleBox().getSampleNumber();
63        }
64
65
66        sampleDescriptionBox = stbl.getSampleDescriptionBox();
67        final List<MovieExtendsBox> movieExtendsBoxes = trackBox.getParent().getBoxes(MovieExtendsBox.class);
68        if (movieExtendsBoxes.size() > 0) {
69            for (MovieExtendsBox mvex : movieExtendsBoxes) {
70                final List<TrackExtendsBox> trackExtendsBoxes = mvex.getBoxes(TrackExtendsBox.class);
71                for (TrackExtendsBox trex : trackExtendsBoxes) {
72                    if (trex.getTrackId() == trackId) {
73                        List<Long> syncSampleList = new LinkedList<Long>();
74
75                        long sampleNumber = 1;
76                        for (MovieFragmentBox movieFragmentBox : trackBox.getIsoFile().getBoxes(MovieFragmentBox.class)) {
77                            List<TrackFragmentBox> trafs = movieFragmentBox.getBoxes(TrackFragmentBox.class);
78                            for (TrackFragmentBox traf : trafs) {
79                                if (traf.getTrackFragmentHeaderBox().getTrackId() == trackId) {
80                                    List<TrackRunBox> truns = traf.getBoxes(TrackRunBox.class);
81                                    for (TrackRunBox trun : truns) {
82                                        final TrackFragmentHeaderBox tfhd = ((TrackFragmentBox) trun.getParent()).getTrackFragmentHeaderBox();
83                                        boolean first = true;
84                                        for (TrackRunBox.Entry entry : trun.getEntries()) {
85                                            if (trun.isSampleDurationPresent()) {
86                                                if (decodingTimeEntries.size() == 0 ||
87                                                        decodingTimeEntries.get(decodingTimeEntries.size() - 1).getDelta() != entry.getSampleDuration()) {
88                                                    decodingTimeEntries.add(new TimeToSampleBox.Entry(1, entry.getSampleDuration()));
89                                                } else {
90                                                    TimeToSampleBox.Entry e = decodingTimeEntries.get(decodingTimeEntries.size() - 1);
91                                                    e.setCount(e.getCount() + 1);
92                                                }
93                                            } else {
94                                                if (tfhd.hasDefaultSampleDuration()) {
95                                                    decodingTimeEntries.add(new TimeToSampleBox.Entry(1, tfhd.getDefaultSampleDuration()));
96                                                } else {
97                                                    decodingTimeEntries.add(new TimeToSampleBox.Entry(1, trex.getDefaultSampleDuration()));
98                                                }
99                                            }
100
101                                            if (trun.isSampleCompositionTimeOffsetPresent()) {
102                                                if (compositionTimeEntries.size() == 0 ||
103                                                        compositionTimeEntries.get(compositionTimeEntries.size() - 1).getOffset() != entry.getSampleCompositionTimeOffset()) {
104                                                    compositionTimeEntries.add(new CompositionTimeToSample.Entry(1, l2i(entry.getSampleCompositionTimeOffset())));
105                                                } else {
106                                                    CompositionTimeToSample.Entry e = compositionTimeEntries.get(compositionTimeEntries.size() - 1);
107                                                    e.setCount(e.getCount() + 1);
108                                                }
109                                            }
110                                            final SampleFlags sampleFlags;
111                                            if (trun.isSampleFlagsPresent()) {
112                                                sampleFlags = entry.getSampleFlags();
113                                            } else {
114                                                if (first && trun.isFirstSampleFlagsPresent()) {
115                                                    sampleFlags = trun.getFirstSampleFlags();
116                                                } else {
117                                                    if (tfhd.hasDefaultSampleFlags()) {
118                                                        sampleFlags = tfhd.getDefaultSampleFlags();
119                                                    } else {
120                                                        sampleFlags = trex.getDefaultSampleFlags();
121                                                    }
122                                                }
123                                            }
124                                            if (sampleFlags != null && !sampleFlags.isSampleIsDifferenceSample()) {
125                                                //iframe
126                                                syncSampleList.add(sampleNumber);
127                                            }
128                                            sampleNumber++;
129                                            first = false;
130                                        }
131                                    }
132                                }
133                            }
134                        }
135                        // Warning: Crappy code
136                        long[] oldSS = syncSamples;
137                        syncSamples = new long[syncSamples.length + syncSampleList.size()];
138                        System.arraycopy(oldSS, 0, syncSamples, 0, oldSS.length);
139                        final Iterator<Long> iterator = syncSampleList.iterator();
140                        int i = oldSS.length;
141                        while (iterator.hasNext()) {
142                            Long syncSampleNumber = iterator.next();
143                            syncSamples[i++] = syncSampleNumber;
144                        }
145                    }
146                }
147            }
148        }
149        MediaHeaderBox mdhd = trackBox.getMediaBox().getMediaHeaderBox();
150        TrackHeaderBox tkhd = trackBox.getTrackHeaderBox();
151
152        setEnabled(tkhd.isEnabled());
153        setInMovie(tkhd.isInMovie());
154        setInPoster(tkhd.isInPoster());
155        setInPreview(tkhd.isInPreview());
156
157        trackMetaData.setTrackId(tkhd.getTrackId());
158        trackMetaData.setCreationTime(DateHelper.convert(mdhd.getCreationTime()));
159        trackMetaData.setLanguage(mdhd.getLanguage());
160/*        System.err.println(mdhd.getModificationTime());
161        System.err.println(DateHelper.convert(mdhd.getModificationTime()));
162        System.err.println(DateHelper.convert(DateHelper.convert(mdhd.getModificationTime())));
163        System.err.println(DateHelper.convert(DateHelper.convert(DateHelper.convert(mdhd.getModificationTime()))));*/
164
165        trackMetaData.setModificationTime(DateHelper.convert(mdhd.getModificationTime()));
166        trackMetaData.setTimescale(mdhd.getTimescale());
167        trackMetaData.setHeight(tkhd.getHeight());
168        trackMetaData.setWidth(tkhd.getWidth());
169        trackMetaData.setLayer(tkhd.getLayer());
170        trackMetaData.setMatrix(tkhd.getMatrix());
171    }
172
173    public List<ByteBuffer> getSamples() {
174        return samples;
175    }
176
177
178    public SampleDescriptionBox getSampleDescriptionBox() {
179        return sampleDescriptionBox;
180    }
181
182    public List<TimeToSampleBox.Entry> getDecodingTimeEntries() {
183        return decodingTimeEntries;
184    }
185
186    public List<CompositionTimeToSample.Entry> getCompositionTimeEntries() {
187        return compositionTimeEntries;
188    }
189
190    public long[] getSyncSamples() {
191        return syncSamples;
192    }
193
194    public List<SampleDependencyTypeBox.Entry> getSampleDependencies() {
195        return sampleDependencies;
196    }
197
198    public TrackMetaData getTrackMetaData() {
199        return trackMetaData;
200    }
201
202    public String getHandler() {
203        return handler;
204    }
205
206    public AbstractMediaHeaderBox getMediaHeaderBox() {
207        return mihd;
208    }
209
210    public SubSampleInformationBox getSubsampleInformationBox() {
211        return null;
212    }
213
214    @Override
215    public String toString() {
216        return "Mp4TrackImpl{" +
217                "handler='" + handler + '\'' +
218                '}';
219    }
220}
221