SampleListpublic class SampleList extends AbstractList Creates a list of ByteBuffer s that represent the samples of a given track. |
Fields Summary |
---|
long[] | offsets | long[] | sizes | com.coremedia.iso.IsoFile | isoFile | MediaDataBox[] | mdats |
Constructors Summary |
---|
public SampleList(TrackBox trackBox)
initIsoFile(trackBox.getIsoFile()); // where are we?
// first we get all sample from the 'normal' MP4 part.
// if there are none - no problem.
SampleSizeBox sampleSizeBox = trackBox.getSampleTableBox().getSampleSizeBox();
ChunkOffsetBox chunkOffsetBox = trackBox.getSampleTableBox().getChunkOffsetBox();
SampleToChunkBox sampleToChunkBox = trackBox.getSampleTableBox().getSampleToChunkBox();
final long[] chunkOffsets = chunkOffsetBox != null ? chunkOffsetBox.getChunkOffsets() : new long[0];
if (sampleToChunkBox != null && sampleToChunkBox.getEntries().size() > 0 &&
chunkOffsets.length > 0 && sampleSizeBox != null && sampleSizeBox.getSampleCount() > 0) {
long[] numberOfSamplesInChunk = sampleToChunkBox.blowup(chunkOffsets.length);
int sampleIndex = 0;
if (sampleSizeBox.getSampleSize() > 0) {
sizes = new long[l2i(sampleSizeBox.getSampleCount())];
Arrays.fill(sizes, sampleSizeBox.getSampleSize());
} else {
sizes = sampleSizeBox.getSampleSizes();
}
offsets = new long[sizes.length];
for (int i = 0; i < numberOfSamplesInChunk.length; i++) {
long thisChunksNumberOfSamples = numberOfSamplesInChunk[i];
long sampleOffset = chunkOffsets[i];
for (int j = 0; j < thisChunksNumberOfSamples; j++) {
long sampleSize = sizes[sampleIndex];
offsets[sampleIndex] = sampleOffset;
sampleOffset += sampleSize;
sampleIndex++;
}
}
}
// Next we add all samples from the fragments
// in most cases - I've never seen it different it's either normal or fragmented.
List<MovieExtendsBox> movieExtendsBoxes = trackBox.getParent().getBoxes(MovieExtendsBox.class);
if (movieExtendsBoxes.size() > 0) {
Map<Long, Long> offsets2Sizes = new HashMap<Long, Long>();
List<TrackExtendsBox> trackExtendsBoxes = movieExtendsBoxes.get(0).getBoxes(TrackExtendsBox.class);
for (TrackExtendsBox trackExtendsBox : trackExtendsBoxes) {
if (trackExtendsBox.getTrackId() == trackBox.getTrackHeaderBox().getTrackId()) {
for (MovieFragmentBox movieFragmentBox : trackBox.getIsoFile().getBoxes(MovieFragmentBox.class)) {
offsets2Sizes.putAll(getOffsets(movieFragmentBox, trackBox.getTrackHeaderBox().getTrackId(), trackExtendsBox));
}
}
}
if (sizes == null || offsets == null) {
sizes = new long[0];
offsets = new long[0];
}
splitToArrays(offsets2Sizes);
}
// We have now a map from all sample offsets to their sizes
| public SampleList(TrackFragmentBox traf)
sizes = new long[0];
offsets = new long[0];
Map<Long, Long> offsets2Sizes = new HashMap<Long, Long>();
initIsoFile(traf.getIsoFile());
final List<MovieFragmentBox> movieFragmentBoxList = isoFile.getBoxes(MovieFragmentBox.class);
final long trackId = traf.getTrackFragmentHeaderBox().getTrackId();
for (MovieFragmentBox moof : movieFragmentBoxList) {
final List<TrackFragmentHeaderBox> trackFragmentHeaderBoxes = moof.getTrackFragmentHeaderBoxes();
for (TrackFragmentHeaderBox tfhd : trackFragmentHeaderBoxes) {
if (tfhd.getTrackId() == trackId) {
offsets2Sizes.putAll(getOffsets(moof, trackId, null));
}
}
}
splitToArrays(offsets2Sizes);
|
Methods Summary |
---|
public java.nio.ByteBuffer | get(int index)
// it is a two stage lookup: from index to offset to size
long offset = offsets[index];
int sampleSize = l2i(sizes[index]);
for (MediaDataBox mediaDataBox : mdats) {
if ((mediaDataBox.getDataStartPosition() <= offset) && (offset + sampleSize <= mediaDataBox.getDataEndPosition())) {
return mediaDataBox.getContent(offset - mediaDataBox.getDataStartPosition(), sampleSize);
}
}
throw new RuntimeException("The sample with offset " + offset + " and size " + l2i(sizes[index]) + " is NOT located within an mdat");
| public long[] | getOffsetKeys()Gets a sorted random access optimized list of all sample offsets.
Basically it is a map from sample number to sample offset.
return offsets;
| java.util.Map | getOffsets(MovieFragmentBox moof, long trackId, TrackExtendsBox trex)
Map<Long, Long> offsets2Sizes = new HashMap<Long, Long>();
List<TrackFragmentBox> traf = moof.getBoxes(TrackFragmentBox.class);
for (TrackFragmentBox trackFragmentBox : traf) {
if (trackFragmentBox.getTrackFragmentHeaderBox().getTrackId() == trackId) {
long baseDataOffset;
if (trackFragmentBox.getTrackFragmentHeaderBox().hasBaseDataOffset()) {
baseDataOffset = trackFragmentBox.getTrackFragmentHeaderBox().getBaseDataOffset();
} else {
baseDataOffset = moof.getOffset();
}
for (TrackRunBox trun : trackFragmentBox.getBoxes(TrackRunBox.class)) {
long sampleBaseOffset = baseDataOffset + trun.getDataOffset();
final TrackFragmentHeaderBox tfhd = ((TrackFragmentBox) trun.getParent()).getTrackFragmentHeaderBox();
long offset = 0;
for (TrackRunBox.Entry entry : trun.getEntries()) {
final long sampleSize;
if (trun.isSampleSizePresent()) {
sampleSize = entry.getSampleSize();
offsets2Sizes.put(offset + sampleBaseOffset, sampleSize);
offset += sampleSize;
} else {
if (tfhd.hasDefaultSampleSize()) {
sampleSize = tfhd.getDefaultSampleSize();
offsets2Sizes.put(offset + sampleBaseOffset, sampleSize);
offset += sampleSize;
} else {
if (trex == null) {
throw new RuntimeException("File doesn't contain trex box but track fragments aren't fully self contained. Cannot determine sample size.");
}
sampleSize = trex.getDefaultSampleSize();
offsets2Sizes.put(offset + sampleBaseOffset, sampleSize);
offset += sampleSize;
}
}
}
}
}
}
return offsets2Sizes;
| private void | initIsoFile(com.coremedia.iso.IsoFile isoFile)
this.isoFile = isoFile;
// find all mdats first to be able to use them later with explicitly looking them up
long currentOffset = 0;
LinkedList<MediaDataBox> mdats = new LinkedList<MediaDataBox>();
for (Box b : this.isoFile.getBoxes()) {
long currentSize = b.getSize();
if ("mdat".equals(b.getType())) {
if (b instanceof MediaDataBox) {
mdats.add((MediaDataBox) b);
} else {
throw new RuntimeException("Sample need to be in mdats and mdats need to be instanceof MediaDataBox");
}
}
currentOffset += currentSize;
}
this.mdats = mdats.toArray(new MediaDataBox[mdats.size()]);
| public int | size()
return sizes.length;
| private void | splitToArrays(java.util.Map offsets2Sizes)
List<Long> keys = new ArrayList<Long>(offsets2Sizes.keySet());
Collections.sort(keys);
long[] nuSizes = new long[sizes.length + keys.size()];
System.arraycopy(sizes, 0, nuSizes, 0, sizes.length);
long[] nuOffsets = new long[offsets.length + keys.size()];
System.arraycopy(offsets, 0, nuOffsets, 0, offsets.length);
for (int i = 0; i < keys.size(); i++) {
nuOffsets[i + offsets.length] = keys.get(i);
nuSizes[i + sizes.length] = offsets2Sizes.get(keys.get(i));
}
sizes = nuSizes;
offsets = nuOffsets;
|
|