package org.jcodec.containers.mp4;
import org.jcodec.common.io.NIOUtils;
import org.jcodec.containers.mp4.boxes.Box;
import org.jcodec.containers.mp4.boxes.ChunkOffsetsBox;
import org.jcodec.containers.mp4.boxes.MediaInfoBox;
import org.jcodec.containers.mp4.boxes.MovieBox;
import org.jcodec.containers.mp4.boxes.NodeBox;
import org.jcodec.containers.mp4.boxes.SampleSizesBox;
import org.jcodec.containers.mp4.boxes.SampleToChunkBox;
import org.jcodec.containers.mp4.boxes.SampleToChunkBox.SampleToChunkEntry;
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
/**
* This class is part of JCodec ( www.jcodec.org ) This software is distributed
* under FreeBSD License
*
* @author The JCodec project
*
*/
public class SampleOffsetUtils {
public static ByteBuffer getSampleData(int sample, File file) throws IOException {
MovieBox moov = MP4Util.parseMovie(file);
MediaInfoBox minf = moov.getAudioTracks().get(0).getMdia().getMinf();
ChunkOffsetsBox stco = NodeBox.findFirstPath(minf, ChunkOffsetsBox.class, Box.path("stbl.stco"));
SampleToChunkBox stsc = NodeBox.findFirstPath(minf, SampleToChunkBox.class, Box.path("stbl.stsc"));
SampleSizesBox stsz = NodeBox.findFirstPath(minf, SampleSizesBox.class, Box.path("stbl.stsz"));
long sampleOffset = getSampleOffset(sample, stsc, stco, stsz);
MappedByteBuffer map = NIOUtils.mapFile(file);
map.position((int) sampleOffset);
map.limit(map.position() + stsz.getSizes()[sample]);
return map;
}
public static long getSampleOffset(int sample, SampleToChunkBox stsc, ChunkOffsetsBox stco, SampleSizesBox stsz) {
int chunkBySample = getChunkBySample(sample, stco, stsc);
int firstSampleAtChunk = getFirstSampleAtChunk(chunkBySample, stsc, stco);
long offset = stco.getChunkOffsets()[chunkBySample - 1];
int[] sizes = stsz.getSizes();
for (int i = firstSampleAtChunk; i < sample; i++) {
offset += sizes[i];
}
return offset;
}
public static int getFirstSampleAtChunk(int chunk, SampleToChunkBox stsc, ChunkOffsetsBox stco) {
int chunks = stco.getChunkOffsets().length;
int samples = 0;
for (int i = 1; i <= chunks; i++) {
if (i == chunk) {
break;
}
int samplesInChunk = getSamplesInChunk(i, stsc);
samples += samplesInChunk;
}
return samples;
}
public static int getChunkBySample(int sampleOfInterest, ChunkOffsetsBox stco, SampleToChunkBox stsc) {
int chunks = stco.getChunkOffsets().length;
int startSample = 0;
int endSample = 0;
for (int i = 1; i <= chunks; i++) {
int samplesInChunk = getSamplesInChunk(i, stsc);
endSample = startSample + samplesInChunk;
if (sampleOfInterest >= startSample && sampleOfInterest < endSample) {
return i;
}
startSample = endSample;
}
return -1;
}
public static int getSamplesInChunk(int chunk, SampleToChunkBox stsc) {
//TODO this is faster with binary search
SampleToChunkEntry[] sampleToChunk = stsc.getSampleToChunk();
int sampleCount = 0;
for (SampleToChunkEntry sampleToChunkEntry : sampleToChunk) {
if (sampleToChunkEntry.getFirst() > chunk) {
return sampleCount;
}
sampleCount = sampleToChunkEntry.getCount();
}
return sampleCount;
}
}