package com.googlecode.mp4parser.authoring.adaptivestreaming; | |
import com.coremedia.iso.boxes.OriginalFormatBox; | |
import com.coremedia.iso.boxes.TimeToSampleBox; | |
import com.coremedia.iso.boxes.sampleentry.SampleEntry; | |
import com.googlecode.mp4parser.authoring.Movie; | |
import com.googlecode.mp4parser.authoring.Track; | |
import com.googlecode.mp4parser.authoring.builder.FragmentIntersectionFinder; | |
import java.io.IOException; | |
import java.nio.ByteBuffer; | |
import java.util.Arrays; | |
import java.util.logging.Logger; | |
import static com.googlecode.mp4parser.util.CastUtils.l2i; | |
/** | |
* Created with IntelliJ IDEA. | |
* User: mstattma | |
* Date: 17.08.12 | |
* Time: 02:51 | |
* To change this template use File | Settings | File Templates. | |
*/ | |
public abstract class AbstractManifestWriter implements ManifestWriter { | |
private static final Logger LOG = Logger.getLogger(AbstractManifestWriter.class.getName()); | |
private FragmentIntersectionFinder intersectionFinder; | |
protected long[] audioFragmentsDurations; | |
protected long[] videoFragmentsDurations; | |
protected AbstractManifestWriter(FragmentIntersectionFinder intersectionFinder) { | |
this.intersectionFinder = intersectionFinder; | |
} | |
/** | |
* Calculates the length of each fragment in the given <code>track</code> (as part of <code>movie</code>). | |
* | |
* @param track target of calculation | |
* @param movie the <code>track</code> must be part of this <code>movie</code> | |
* @return the duration of each fragment in track timescale | |
*/ | |
public long[] calculateFragmentDurations(Track track, Movie movie) { | |
long[] startSamples = intersectionFinder.sampleNumbers(track, movie); | |
long[] durations = new long[startSamples.length]; | |
int currentFragment = 0; | |
int currentSample = 1; // sync samples start with 1 ! | |
for (TimeToSampleBox.Entry entry : track.getDecodingTimeEntries()) { | |
for (int max = currentSample + l2i(entry.getCount()); currentSample < max; currentSample++) { | |
// in this loop we go through the entry.getCount() samples starting from current sample. | |
// the next entry.getCount() samples have the same decoding time. | |
if (currentFragment != startSamples.length - 1 && currentSample == startSamples[currentFragment + 1]) { | |
// we are not in the last fragment && the current sample is the start sample of the next fragment | |
currentFragment++; | |
} | |
durations[currentFragment] += entry.getDelta(); | |
} | |
} | |
return durations; | |
} | |
public long getBitrate(Track track) { | |
long bitrate = 0; | |
for (ByteBuffer sample : track.getSamples()) { | |
bitrate += sample.limit(); | |
} | |
bitrate *= 8; // from bytes to bits | |
bitrate /= ((double) getDuration(track)) / track.getTrackMetaData().getTimescale(); // per second | |
return bitrate; | |
} | |
protected static long getDuration(Track track) { | |
long duration = 0; | |
for (TimeToSampleBox.Entry entry : track.getDecodingTimeEntries()) { | |
duration += entry.getCount() * entry.getDelta(); | |
} | |
return duration; | |
} | |
protected long[] checkFragmentsAlign(long[] referenceTimes, long[] checkTimes) throws IOException { | |
if (referenceTimes == null || referenceTimes.length == 0) { | |
return checkTimes; | |
} | |
long[] referenceTimesMinusLast = new long[referenceTimes.length - 1]; | |
System.arraycopy(referenceTimes, 0, referenceTimesMinusLast, 0, referenceTimes.length - 1); | |
long[] checkTimesMinusLast = new long[checkTimes.length - 1]; | |
System.arraycopy(checkTimes, 0, checkTimesMinusLast, 0, checkTimes.length - 1); | |
if (!Arrays.equals(checkTimesMinusLast, referenceTimesMinusLast)) { | |
String log = ""; | |
log += (referenceTimes.length); | |
log += ("Reference : ["); | |
for (long l : referenceTimes) { | |
log += (String.format("%10d,", l)); | |
} | |
log += ("]"); | |
LOG.warning(log); | |
log = ""; | |
log += (checkTimes.length); | |
log += ("Current : ["); | |
for (long l : checkTimes) { | |
log += (String.format("%10d,", l)); | |
} | |
log += ("]"); | |
LOG.warning(log); | |
throw new IOException("Track does not have the same fragment borders as its predecessor."); | |
} else { | |
return checkTimes; | |
} | |
} | |
protected String getFormat(SampleEntry se) { | |
String type = se.getType(); | |
if (type.equals("encv") || type.equals("enca") || type.equals("encv")) { | |
OriginalFormatBox frma = se.getBoxes(OriginalFormatBox.class, true).get(0); | |
type = frma.getDataFormat(); | |
} | |
return type; | |
} | |
} |