1package com.googlecode.mp4parser.authoring.adaptivestreaming;
2
3import com.coremedia.iso.boxes.OriginalFormatBox;
4import com.coremedia.iso.boxes.TimeToSampleBox;
5import com.coremedia.iso.boxes.sampleentry.SampleEntry;
6import com.googlecode.mp4parser.authoring.Movie;
7import com.googlecode.mp4parser.authoring.Track;
8import com.googlecode.mp4parser.authoring.builder.FragmentIntersectionFinder;
9
10import java.io.IOException;
11import java.nio.ByteBuffer;
12import java.util.Arrays;
13import java.util.logging.Logger;
14
15import static com.googlecode.mp4parser.util.CastUtils.l2i;
16
17/**
18 * Created with IntelliJ IDEA.
19 * User: mstattma
20 * Date: 17.08.12
21 * Time: 02:51
22 * To change this template use File | Settings | File Templates.
23 */
24public abstract class AbstractManifestWriter implements ManifestWriter {
25    private static final Logger LOG = Logger.getLogger(AbstractManifestWriter.class.getName());
26
27    private FragmentIntersectionFinder intersectionFinder;
28    protected long[] audioFragmentsDurations;
29    protected long[] videoFragmentsDurations;
30
31    protected AbstractManifestWriter(FragmentIntersectionFinder intersectionFinder) {
32        this.intersectionFinder = intersectionFinder;
33    }
34
35    /**
36     * Calculates the length of each fragment in the given <code>track</code> (as part of <code>movie</code>).
37     *
38     * @param track target of calculation
39     * @param movie the <code>track</code> must be part of this <code>movie</code>
40     * @return the duration of each fragment in track timescale
41     */
42    public long[] calculateFragmentDurations(Track track, Movie movie) {
43        long[] startSamples = intersectionFinder.sampleNumbers(track, movie);
44        long[] durations = new long[startSamples.length];
45        int currentFragment = 0;
46        int currentSample = 1; // sync samples start with 1 !
47
48        for (TimeToSampleBox.Entry entry : track.getDecodingTimeEntries()) {
49            for (int max = currentSample + l2i(entry.getCount()); currentSample < max; currentSample++) {
50                // in this loop we go through the entry.getCount() samples starting from current sample.
51                // the next entry.getCount() samples have the same decoding time.
52                if (currentFragment != startSamples.length - 1 && currentSample == startSamples[currentFragment + 1]) {
53                    // we are not in the last fragment && the current sample is the start sample of the next fragment
54                    currentFragment++;
55                }
56                durations[currentFragment] += entry.getDelta();
57
58
59            }
60        }
61        return durations;
62
63    }
64
65    public long getBitrate(Track track) {
66        long bitrate = 0;
67        for (ByteBuffer sample : track.getSamples()) {
68            bitrate += sample.limit();
69        }
70        bitrate *= 8; // from bytes to bits
71        bitrate /= ((double) getDuration(track)) / track.getTrackMetaData().getTimescale(); // per second
72        return bitrate;
73    }
74
75    protected static long getDuration(Track track) {
76        long duration = 0;
77        for (TimeToSampleBox.Entry entry : track.getDecodingTimeEntries()) {
78            duration += entry.getCount() * entry.getDelta();
79        }
80        return duration;
81    }
82
83    protected long[] checkFragmentsAlign(long[] referenceTimes, long[] checkTimes) throws IOException {
84
85        if (referenceTimes == null || referenceTimes.length == 0) {
86            return checkTimes;
87        }
88        long[] referenceTimesMinusLast = new long[referenceTimes.length - 1];
89        System.arraycopy(referenceTimes, 0, referenceTimesMinusLast, 0, referenceTimes.length - 1);
90        long[] checkTimesMinusLast = new long[checkTimes.length - 1];
91        System.arraycopy(checkTimes, 0, checkTimesMinusLast, 0, checkTimes.length - 1);
92
93        if (!Arrays.equals(checkTimesMinusLast, referenceTimesMinusLast)) {
94            String log = "";
95            log += (referenceTimes.length);
96            log += ("Reference     :  [");
97            for (long l : referenceTimes) {
98                log += (String.format("%10d,", l));
99            }
100            log += ("]");
101            LOG.warning(log);
102            log = "";
103
104            log += (checkTimes.length);
105            log += ("Current       :  [");
106            for (long l : checkTimes) {
107                log += (String.format("%10d,", l));
108            }
109            log += ("]");
110            LOG.warning(log);
111            throw new IOException("Track does not have the same fragment borders as its predecessor.");
112
113        } else {
114            return checkTimes;
115        }
116    }
117
118    protected String getFormat(SampleEntry se) {
119        String type = se.getType();
120        if (type.equals("encv") || type.equals("enca") || type.equals("encv")) {
121            OriginalFormatBox frma = se.getBoxes(OriginalFormatBox.class, true).get(0);
122            type = frma.getDataFormat();
123        }
124        return type;
125    }
126}
127