protected Box createStbl(Track track, Movie movie, Map<Track, int[]> chunks) { SampleTableBox stbl = new SampleTableBox(); createStsd(track, stbl); createStts(track, stbl); createCtts(track, stbl); createStss(track, stbl); createSdtp(track, stbl); createStsc(track, chunks, stbl); createStsz(track, stbl); createStco(track, movie, chunks, stbl); Map<String, List<GroupEntry>> groupEntryFamilies = new HashMap<String, List<GroupEntry>>(); for (Map.Entry<GroupEntry, long[]> sg : track.getSampleGroups().entrySet()) { String type = sg.getKey().getType(); List<GroupEntry> groupEntries = groupEntryFamilies.get(type); if (groupEntries == null) { groupEntries = new ArrayList<GroupEntry>(); groupEntryFamilies.put(type, groupEntries); } groupEntries.add(sg.getKey()); } for (Map.Entry<String, List<GroupEntry>> sg : groupEntryFamilies.entrySet()) { SampleGroupDescriptionBox sgdb = new SampleGroupDescriptionBox(); String type = sg.getKey(); sgdb.setGroupEntries(sg.getValue()); SampleToGroupBox sbgp = new SampleToGroupBox(); sbgp.setGroupingType(type); SampleToGroupBox.Entry last = null; for (int i = 0; i < track.getSamples().size(); i++) { int index = 0; for (int j = 0; j < sg.getValue().size(); j++) { GroupEntry groupEntry = sg.getValue().get(j); long[] sampleNums = track.getSampleGroups().get(groupEntry); if (Arrays.binarySearch(sampleNums, i) >= 0) { index = j + 1; } } if (last == null || last.getGroupDescriptionIndex() != index) { last = new SampleToGroupBox.Entry(1, index); sbgp.getEntries().add(last); } else { last.setSampleCount(last.getSampleCount() + 1); } } stbl.addBox(sgdb); stbl.addBox(sbgp); } if (track instanceof CencEncryptedTrack) { createCencBoxes((CencEncryptedTrack) track, stbl, chunks.get(track)); } createSubs(track, stbl); return stbl; }
protected void createSdtp(Track track, SampleTableBox stbl) { if (track.getSampleDependencies() != null && !track.getSampleDependencies().isEmpty()) { SampleDependencyTypeBox sdtp = new SampleDependencyTypeBox(); sdtp.setEntries(track.getSampleDependencies()); stbl.addBox(sdtp); } }
protected void createStss(Track track, SampleTableBox stbl) { long[] syncSamples = track.getSyncSamples(); if (syncSamples != null && syncSamples.length > 0) { SyncSampleBox stss = new SyncSampleBox(); stss.setSampleNumber(syncSamples); stbl.addBox(stss); } }
protected void createCtts(Track track, SampleTableBox stbl) { List<CompositionTimeToSample.Entry> compositionTimeToSampleEntries = track.getCompositionTimeEntries(); if (compositionTimeToSampleEntries != null && !compositionTimeToSampleEntries.isEmpty()) { CompositionTimeToSample ctts = new CompositionTimeToSample(); ctts.setEntries(compositionTimeToSampleEntries); stbl.addBox(ctts); } }
protected void createCencBoxes(CencEncryptedTrack track, SampleTableBox stbl, int[] chunkSizes) { SampleAuxiliaryInformationSizesBox saiz = new SampleAuxiliaryInformationSizesBox(); saiz.setAuxInfoType("cenc"); saiz.setFlags(1); List<CencSampleAuxiliaryDataFormat> sampleEncryptionEntries = track.getSampleEncryptionEntries(); if (track.hasSubSampleEncryption()) { short[] sizes = new short[sampleEncryptionEntries.size()]; for (int i = 0; i < sizes.length; i++) { sizes[i] = (short) sampleEncryptionEntries.get(i).getSize(); } saiz.setSampleInfoSizes(sizes); } else { saiz.setDefaultSampleInfoSize(8); // 8 bytes iv saiz.setSampleCount(track.getSamples().size()); } SampleAuxiliaryInformationOffsetsBox saio = new SampleAuxiliaryInformationOffsetsBox(); SampleEncryptionBox senc = new SampleEncryptionBox(); senc.setSubSampleEncryption(track.hasSubSampleEncryption()); senc.setEntries(sampleEncryptionEntries); long offset = senc.getOffsetToFirstIV(); int index = 0; long[] offsets = new long[chunkSizes.length]; for (int i = 0; i < chunkSizes.length; i++) { offsets[i] = offset; for (int j = 0; j < chunkSizes[i]; j++) { offset += sampleEncryptionEntries.get(index++).getSize(); } } saio.setOffsets(offsets); stbl.addBox(saiz); stbl.addBox(saio); stbl.addBox(senc); sampleAuxiliaryInformationOffsetsBoxes.add(saio); }
protected void createStts(Track track, SampleTableBox stbl) { TimeToSampleBox.Entry lastEntry = null; List<TimeToSampleBox.Entry> entries = new ArrayList<TimeToSampleBox.Entry>(); for (long delta : track.getSampleDurations()) { if (lastEntry != null && lastEntry.getDelta() == delta) { lastEntry.setCount(lastEntry.getCount() + 1); } else { lastEntry = new TimeToSampleBox.Entry(1, delta); entries.add(lastEntry); } } TimeToSampleBox stts = new TimeToSampleBox(); stts.setEntries(entries); stbl.addBox(stts); }
protected void createStsc(Track track, Map<Track, int[]> chunks, SampleTableBox stbl) { int[] tracksChunkSizes = chunks.get(track); SampleToChunkBox stsc = new SampleToChunkBox(); stsc.setEntries(new LinkedList<SampleToChunkBox.Entry>()); long lastChunkSize = Integer.MIN_VALUE; // to be sure the first chunks hasn't got the same size for (int i = 0; i < tracksChunkSizes.length; i++) { // The sample description index references the sample description box // that describes the samples of this chunk. My Tracks cannot have more // than one sample description box. Therefore 1 is always right // the first chunk has the number '1' if (lastChunkSize != tracksChunkSizes[i]) { stsc.getEntries().add(new SampleToChunkBox.Entry(i + 1, tracksChunkSizes[i], 1)); lastChunkSize = tracksChunkSizes[i]; } } stbl.addBox(stsc); }
protected void createStsz(Track track, SampleTableBox stbl) { SampleSizeBox stsz = new SampleSizeBox(); stsz.setSampleSizes(track2SampleSizes.get(track)); stbl.addBox(stsz); }
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)); }
protected void createStsd(Track track, SampleTableBox stbl) { stbl.addBox(track.getSampleDescriptionBox()); }
protected void createSubs(Track track, SampleTableBox stbl) { if (track.getSubsampleInformationBox() != null) { stbl.addBox(track.getSubsampleInformationBox()); } }