package mp4.util;
import java.io.DataOutputStream;
import java.io.IOException;
import mp4.util.atom.MdatAtom;
import mp4.util.atom.MoovAtom;
import mp4.util.atom.TrakAtom;
public class Mp4InterleaveWriter {
MoovAtom moov;
MoovAtom newMoov;
MdatAtom mdat;
long firstChunkOffset;
public static final float interleaveTime = .4f;
public Mp4InterleaveWriter(MoovAtom moov,
MdatAtom mdat, long firstChunkOffset) {
this.moov = moov;
this.mdat = mdat;
this.firstChunkOffset = firstChunkOffset;
}
public static interface InterleaveChunkHandler {
void addChunk(int trakNum, long chunkNum, long chunkSize) throws IOException;
}
private void computeChunkPositions(InterleaveChunkHandler handler) throws IOException {
int numTraks = moov.getTrackCount();
long[] sampleNumber = new long [numTraks];
long[] sampleCount = new long[numTraks];
int curTrak = 0;
for (int i=0;i<numTraks;i++) {
sampleNumber[i] = 1;
sampleCount[i] = moov.getTrack(i).getMdia().getMinf().getStbl().getStts().getTotalSampleCount();
if (moov.getTrack(i).getMdia().getHdlr().isSound())
curTrak = i;
}
long total = 0;
float lastCts = 0;
while(true) {
// Find trak with lowest dts
int minTrak = Integer.MAX_VALUE;
float minCts = Float.MAX_VALUE;
for (int i=0;i<numTraks;i++) {
if (sampleNumber[i] >= sampleCount[i]) {
if (curTrak == i)
curTrak = -1;
continue;
}
long cts = moov.getTrack(i).getMdia().getMinf().getStbl().getStts().sampleToTime(sampleNumber[i]);
float mcts = cts / moov.getTrack(i).getMdia().getMdhd().getTimeScale();
if (mcts < minCts) {
minCts = mcts;
minTrak = i;
}
}
if (minTrak >= numTraks)
break;
if (curTrak == -1 || lastCts - minCts > interleaveTime) {
curTrak = minTrak;
lastCts = minCts;
} else
lastCts = moov.getTrack(curTrak).getMdia().getMinf().getStbl().getStts().sampleToTime(sampleNumber[curTrak]) / moov.getTrack(curTrak).getMdia().getMdhd().getTimeScale();
// Write out chunk containing the sample
TrakAtom trak = moov.getTrack(curTrak);
long chunk = trak.getMdia().getMinf().getStbl().getStsc().sampleToChunk(sampleNumber[curTrak]);
// compute the chunk size
long sampleCnt = 0;
long chunkSize = 0;
for (long sample=sampleNumber[curTrak];sample <= sampleCount[curTrak] && chunk == trak.getMdia().getMinf().getStbl().getStsc().sampleToChunk(sample);sample++) {
sampleCnt++;
chunkSize += trak.getMdia().getMinf().getStbl().getStsz().getSampleSize(sample);
}
//MP4Log.log("Trak: " + minTrak + ", Chunk: " + chunk + ", Size: " + chunkSize);
handler.addChunk(curTrak, chunk, chunkSize);
sampleNumber[curTrak] += sampleCnt;
total += chunkSize;
}
MP4Log.log("Total size of all mdat chunks: " + total);
}
public void calcInterleave() throws IOException {
newMoov = moov.cut(0);
long start;
start = System.currentTimeMillis();
MP4Log.log("Start reinterleave...");
computeChunkPositions(new InterleaveChunkHandler() {
long offset = firstChunkOffset;
public void addChunk(int trakNum, long chunkNum, long chunkSize) {
newMoov.getTrack(trakNum).getMdia().getMinf().getStbl().getStco().setChunkOffset((int)chunkNum-1, offset);
offset += chunkSize;
}
});
MP4Log.log("Finished reinterleave in: " + (System.currentTimeMillis() - start) / 1000.0 + "s");
}
public void write(final DataOutputStream dos, boolean writeMdat) throws IOException {
newMoov.writeData(dos);
if (writeMdat) {
mdat.setSize(0);
mdat.writeHeader(dos);
computeChunkPositions(new InterleaveChunkHandler() {
public void addChunk(int trakNum, long chunkNum, long chunkSize) throws IOException {
long offset = moov.getTrack(trakNum).getMdia().getMinf().getStbl().getStco().getChunkOffset((int)chunkNum);
mdat.writeChunk(dos, offset-firstChunkOffset, chunkSize);
}
});
}
}
}