protected void createStco( Track targetTrack, Movie movie, Map<Track, int[]> chunks, SampleTableBox stbl) { if (chunkOffsetBoxes.get(targetTrack) == null) { // The ChunkOffsetBox we create here is just a stub // since we haven't created the whole structure we can't tell where the // first chunk starts (mdat box). So I just let the chunk offset // start at zero and I will add the mdat offset later. long offset = 0; // all tracks have the same number of chunks if (LOG.isLoggable(Level.FINE)) { LOG.fine( "Calculating chunk offsets for track_" + targetTrack.getTrackMetaData().getTrackId()); } List<Track> tracks = new ArrayList<Track>(chunks.keySet()); Collections.sort( tracks, new Comparator<Track>() { public int compare(Track o1, Track o2) { return l2i(o1.getTrackMetaData().getTrackId() - o2.getTrackMetaData().getTrackId()); } }); Map<Track, Integer> trackToChunk = new HashMap<Track, Integer>(); Map<Track, Integer> trackToSample = new HashMap<Track, Integer>(); Map<Track, Double> trackToTime = new HashMap<Track, Double>(); for (Track track : tracks) { trackToChunk.put(track, 0); trackToSample.put(track, 0); trackToTime.put(track, 0.0); chunkOffsetBoxes.put(track, new StaticChunkOffsetBox()); } while (true) { Track nextChunksTrack = null; for (Track track : tracks) { // This always chooses the least progressed track if ((nextChunksTrack == null || trackToTime.get(track) < trackToTime.get(nextChunksTrack)) && // either first OR track's next chunk's starttime is smaller than nextTrack's next // chunks starttime // AND their need to be chunks left! (trackToChunk.get(track) < chunks.get(track).length)) { nextChunksTrack = track; } } if (nextChunksTrack == null) { break; // no next } // found the next one ChunkOffsetBox chunkOffsetBox = chunkOffsetBoxes.get(nextChunksTrack); chunkOffsetBox.setChunkOffsets( Mp4Arrays.copyOfAndAppend(chunkOffsetBox.getChunkOffsets(), offset)); int nextChunksIndex = trackToChunk.get(nextChunksTrack); int numberOfSampleInNextChunk = chunks.get(nextChunksTrack)[nextChunksIndex]; int startSample = trackToSample.get(nextChunksTrack); double time = trackToTime.get(nextChunksTrack); for (int j = startSample; j < startSample + numberOfSampleInNextChunk; j++) { offset += track2SampleSizes.get(nextChunksTrack)[j]; time += (double) nextChunksTrack.getSampleDurations()[j] / nextChunksTrack.getTrackMetaData().getTimescale(); } trackToChunk.put(nextChunksTrack, nextChunksIndex + 1); trackToSample.put(nextChunksTrack, startSample + numberOfSampleInNextChunk); trackToTime.put(nextChunksTrack, time); } } stbl.addBox(chunkOffsetBoxes.get(targetTrack)); }
public SampleList(TrackBox trackBox) { this.isoFile = trackBox.getIsoFile(); // where are we? offsets2Sizes = new HashMap<Long, Long>(); // find all mdats first to be able to use them later with explicitly looking them up long currentOffset = 0; for (Box b : isoFile.getBoxes()) { long currentSize = b.getSize(); if ("mdat".equals(b.getType())) { if (b instanceof MediaDataBox) { long contentOffset = currentOffset + ((MediaDataBox) b).getHeader().limit(); mdatStartCache.put((MediaDataBox) b, contentOffset); mdatEndCache.put((MediaDataBox) b, contentOffset + currentSize); mdats.add((MediaDataBox) b); } else { throw new RuntimeException( "Sample need to be in mdats and mdats need to be instanceof MediaDataBox"); } } currentOffset += currentSize; } // 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(); if (sampleToChunkBox != null && sampleToChunkBox.getEntries().size() > 0 && chunkOffsetBox != null && chunkOffsetBox.getChunkOffsets().length > 0 && sampleSizeBox != null && sampleSizeBox.getSampleCount() > 0) { long[] numberOfSamplesInChunk = sampleToChunkBox.blowup(chunkOffsetBox.getChunkOffsets().length); if (sampleSizeBox.getSampleSize() > 0) { // Every sample has the same size! // no need to store each size separately // this happens when people use raw audio formats in MP4 (are you stupid guys???) offsets2Sizes = new DummyMap<Long, Long>(sampleSizeBox.getSampleSize()); long sampleSize = sampleSizeBox.getSampleSize(); for (int i = 0; i < numberOfSamplesInChunk.length; i++) { long thisChunksNumberOfSamples = numberOfSamplesInChunk[i]; long sampleOffset = chunkOffsetBox.getChunkOffsets()[i]; for (int j = 0; j < thisChunksNumberOfSamples; j++) { offsets2Sizes.put(sampleOffset, sampleSize); sampleOffset += sampleSize; } } } else { // the normal case where all samples have different sizes int sampleIndex = 0; long sampleSizes[] = sampleSizeBox.getSampleSizes(); for (int i = 0; i < numberOfSamplesInChunk.length; i++) { long thisChunksNumberOfSamples = numberOfSamplesInChunk[i]; long sampleOffset = chunkOffsetBox.getChunkOffsets()[i]; for (int j = 0; j < thisChunksNumberOfSamples; j++) { long sampleSize = sampleSizes[sampleIndex]; offsets2Sizes.put(sampleOffset, sampleSize); 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) { 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())); } } } } // We have now a map from all sample offsets to their sizes }