diff options
Diffstat (limited to 'isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/.svn')
9 files changed, 0 insertions, 2170 deletions
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/.svn/all-wcprops b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/.svn/all-wcprops deleted file mode 100644 index 9204edf..0000000 --- a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/.svn/all-wcprops +++ /dev/null @@ -1,47 +0,0 @@ -K 25 -svn:wc:ra_dav:version-url -V 90 -/svn/!svn/ver/776/trunk/isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder -END -FragmentIntersectionFinder.java -K 25 -svn:wc:ra_dav:version-url -V 122 -/svn/!svn/ver/455/trunk/isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/FragmentIntersectionFinder.java -END -TwoSecondIntersectionFinder.java -K 25 -svn:wc:ra_dav:version-url -V 123 -/svn/!svn/ver/658/trunk/isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/TwoSecondIntersectionFinder.java -END -FragmentedMp4Builder.java -K 25 -svn:wc:ra_dav:version-url -V 116 -/svn/!svn/ver/707/trunk/isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/FragmentedMp4Builder.java -END -Mp4Builder.java -K 25 -svn:wc:ra_dav:version-url -V 106 -/svn/!svn/ver/672/trunk/isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/Mp4Builder.java -END -SyncSampleIntersectFinderImpl.java -K 25 -svn:wc:ra_dav:version-url -V 125 -/svn/!svn/ver/774/trunk/isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/SyncSampleIntersectFinderImpl.java -END -DefaultMp4Builder.java -K 25 -svn:wc:ra_dav:version-url -V 113 -/svn/!svn/ver/776/trunk/isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/DefaultMp4Builder.java -END -ByteBufferHelper.java -K 25 -svn:wc:ra_dav:version-url -V 112 -/svn/!svn/ver/530/trunk/isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/ByteBufferHelper.java -END diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/.svn/entries b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/.svn/entries deleted file mode 100644 index 2c6e266..0000000 --- a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/.svn/entries +++ /dev/null @@ -1,266 +0,0 @@ -10 - -dir -778 -http://mp4parser.googlecode.com/svn/trunk/isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder -http://mp4parser.googlecode.com/svn - - - -2012-09-10T14:34:23.574807Z -776 -sebastian.annies@gmail.com - - - - - - - - - - - - - - -7decde4b-c250-0410-a0da-51896bc88be6 - -FragmentIntersectionFinder.java -file - - - - -2012-09-14T17:27:50.237215Z -979df582987d3797c42ae315b3d2555a -2012-04-10T10:20:46.357991Z -455 -Sebastian.Annies@gmail.com - - - - - - - - - - - - - - - - - - - - - -1160 - -TwoSecondIntersectionFinder.java -file - - - - -2012-09-14T17:27:50.237215Z -47aa683919ce24da51b82236e8f27426 -2012-06-06T10:36:10.590512Z -658 -Sebastian.Annies@gmail.com - - - - - - - - - - - - - - - - - - - - - -2817 - -FragmentedMp4Builder.java -file - - - - -2012-09-14T17:27:50.237215Z -f81cbeb22aee5ab0405ad38e67b9e4e1 -2012-07-10T16:32:53.757675Z -707 -michael.stattmann@gmail.com - - - - - - - - - - - - - - - - - - - - - -31005 - -Mp4Builder.java -file - - - - -2012-09-14T17:27:50.237215Z -ea548a5ace2a4480472b90d9f820bceb -2012-06-11T22:10:18.183835Z -672 -michael.stattmann@gmail.com - - - - - - - - - - - - - - - - - - - - - -1156 - -SyncSampleIntersectFinderImpl.java -file - - - - -2012-09-14T17:27:50.237215Z -d49823bb95a5920f2df6899195c28a72 -2012-09-06T12:33:39.419429Z -774 -michael.stattmann@gmail.com - - - - - - - - - - - - - - - - - - - - - -14915 - -DefaultMp4Builder.java -file - - - - -2012-09-14T17:27:50.237215Z -15d93ea29e24e257604301ad5e73d623 -2012-09-10T14:34:23.574807Z -776 -sebastian.annies@gmail.com - - - - - - - - - - - - - - - - - - - - - -22096 - -ByteBufferHelper.java -file - - - - -2012-09-14T17:27:50.237215Z -a103511eeddf1e0d1962704ae23ba4a5 -2012-04-27T09:17:25.544414Z -530 -hoemmagnus@gmail.com - - - - - - - - - - - - - - - - - - - - - -2344 - diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/.svn/text-base/ByteBufferHelper.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/.svn/text-base/ByteBufferHelper.java.svn-base deleted file mode 100644 index ad21b11..0000000 --- a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/.svn/text-base/ByteBufferHelper.java.svn-base +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2012 Sebastian Annies, Hamburg - * - * Licensed under the Apache License, Version 2.0 (the License); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an AS IS BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.googlecode.mp4parser.authoring.builder; - -import java.nio.ByteBuffer; -import java.nio.MappedByteBuffer; -import java.util.ArrayList; -import java.util.List; - -/** - * Used to merge adjacent byte buffers. - */ -public class ByteBufferHelper { - public static List<ByteBuffer> mergeAdjacentBuffers(List<ByteBuffer> samples) { - ArrayList<ByteBuffer> nuSamples = new ArrayList<ByteBuffer>(samples.size()); - for (ByteBuffer buffer : samples) { - int lastIndex = nuSamples.size() - 1; - if (lastIndex >= 0 && buffer.hasArray() && nuSamples.get(lastIndex).hasArray() && buffer.array() == nuSamples.get(lastIndex).array() && - nuSamples.get(lastIndex).arrayOffset() + nuSamples.get(lastIndex).limit() == buffer.arrayOffset()) { - ByteBuffer oldBuffer = nuSamples.remove(lastIndex); - ByteBuffer nu = ByteBuffer.wrap(buffer.array(), oldBuffer.arrayOffset(), oldBuffer.limit() + buffer.limit()).slice(); - // We need to slice here since wrap([], offset, length) just sets position and not the arrayOffset. - nuSamples.add(nu); - } else if (lastIndex >= 0 && - buffer instanceof MappedByteBuffer && nuSamples.get(lastIndex) instanceof MappedByteBuffer && - nuSamples.get(lastIndex).limit() == nuSamples.get(lastIndex).capacity() - buffer.capacity()) { - // This can go wrong - but will it? - ByteBuffer oldBuffer = nuSamples.get(lastIndex); - oldBuffer.limit(buffer.limit() + oldBuffer.limit()); - } else { - buffer.rewind(); - nuSamples.add(buffer); - } - } - return nuSamples; - } -} diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/.svn/text-base/DefaultMp4Builder.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/.svn/text-base/DefaultMp4Builder.java.svn-base deleted file mode 100644 index 9bd1ca6..0000000 --- a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/.svn/text-base/DefaultMp4Builder.java.svn-base +++ /dev/null @@ -1,576 +0,0 @@ -/* - * Copyright 2012 Sebastian Annies, Hamburg - * - * Licensed under the Apache License, Version 2.0 (the License); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an AS IS BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.googlecode.mp4parser.authoring.builder; - -import com.coremedia.iso.BoxParser; -import com.coremedia.iso.IsoFile; -import com.coremedia.iso.IsoTypeWriter; -import com.coremedia.iso.boxes.Box; -import com.coremedia.iso.boxes.CompositionTimeToSample; -import com.coremedia.iso.boxes.ContainerBox; -import com.coremedia.iso.boxes.DataEntryUrlBox; -import com.coremedia.iso.boxes.DataInformationBox; -import com.coremedia.iso.boxes.DataReferenceBox; -import com.coremedia.iso.boxes.FileTypeBox; -import com.coremedia.iso.boxes.HandlerBox; -import com.coremedia.iso.boxes.MediaBox; -import com.coremedia.iso.boxes.MediaHeaderBox; -import com.coremedia.iso.boxes.MediaInformationBox; -import com.coremedia.iso.boxes.MovieBox; -import com.coremedia.iso.boxes.MovieHeaderBox; -import com.coremedia.iso.boxes.SampleDependencyTypeBox; -import com.coremedia.iso.boxes.SampleSizeBox; -import com.coremedia.iso.boxes.SampleTableBox; -import com.coremedia.iso.boxes.SampleToChunkBox; -import com.coremedia.iso.boxes.StaticChunkOffsetBox; -import com.coremedia.iso.boxes.SyncSampleBox; -import com.coremedia.iso.boxes.TimeToSampleBox; -import com.coremedia.iso.boxes.TrackBox; -import com.coremedia.iso.boxes.TrackHeaderBox; -import com.googlecode.mp4parser.authoring.DateHelper; -import com.googlecode.mp4parser.authoring.Movie; -import com.googlecode.mp4parser.authoring.Track; - -import java.io.IOException; -import java.nio.ByteBuffer; -import java.nio.MappedByteBuffer; -import java.nio.channels.GatheringByteChannel; -import java.nio.channels.ReadableByteChannel; -import java.nio.channels.WritableByteChannel; -import java.util.ArrayList; -import java.util.Date; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.logging.Level; -import java.util.logging.Logger; - -import static com.googlecode.mp4parser.util.CastUtils.l2i; - -/** - * Creates a plain MP4 file from a video. Plain as plain can be. - */ -public class DefaultMp4Builder implements Mp4Builder { - - public int STEPSIZE = 64; - Set<StaticChunkOffsetBox> chunkOffsetBoxes = new HashSet<StaticChunkOffsetBox>(); - private static Logger LOG = Logger.getLogger(DefaultMp4Builder.class.getName()); - - HashMap<Track, List<ByteBuffer>> track2Sample = new HashMap<Track, List<ByteBuffer>>(); - HashMap<Track, long[]> track2SampleSizes = new HashMap<Track, long[]>(); - private FragmentIntersectionFinder intersectionFinder = new TwoSecondIntersectionFinder(); - - public void setIntersectionFinder(FragmentIntersectionFinder intersectionFinder) { - this.intersectionFinder = intersectionFinder; - } - - /** - * {@inheritDoc} - */ - public IsoFile build(Movie movie) { - LOG.fine("Creating movie " + movie); - for (Track track : movie.getTracks()) { - // getting the samples may be a time consuming activity - List<ByteBuffer> samples = track.getSamples(); - putSamples(track, samples); - long[] sizes = new long[samples.size()]; - for (int i = 0; i < sizes.length; i++) { - sizes[i] = samples.get(i).limit(); - } - putSampleSizes(track, sizes); - } - - IsoFile isoFile = new IsoFile(); - // ouch that is ugly but I don't know how to do it else - List<String> minorBrands = new LinkedList<String>(); - minorBrands.add("isom"); - minorBrands.add("iso2"); - minorBrands.add("avc1"); - - isoFile.addBox(new FileTypeBox("isom", 0, minorBrands)); - isoFile.addBox(createMovieBox(movie)); - InterleaveChunkMdat mdat = new InterleaveChunkMdat(movie); - isoFile.addBox(mdat); - - /* - dataOffset is where the first sample starts. In this special mdat the samples always start - at offset 16 so that we can use the same offset for large boxes and small boxes - */ - long dataOffset = mdat.getDataOffset(); - for (StaticChunkOffsetBox chunkOffsetBox : chunkOffsetBoxes) { - long[] offsets = chunkOffsetBox.getChunkOffsets(); - for (int i = 0; i < offsets.length; i++) { - offsets[i] += dataOffset; - } - } - - - return isoFile; - } - - public FragmentIntersectionFinder getFragmentIntersectionFinder() { - throw new UnsupportedOperationException("No fragment intersection finder in default MP4 builder!"); - } - - protected long[] putSampleSizes(Track track, long[] sizes) { - return track2SampleSizes.put(track, sizes); - } - - protected List<ByteBuffer> putSamples(Track track, List<ByteBuffer> samples) { - return track2Sample.put(track, samples); - } - - private MovieBox createMovieBox(Movie movie) { - MovieBox movieBox = new MovieBox(); - MovieHeaderBox mvhd = new MovieHeaderBox(); - - mvhd.setCreationTime(DateHelper.convert(new Date())); - mvhd.setModificationTime(DateHelper.convert(new Date())); - - long movieTimeScale = getTimescale(movie); - long duration = 0; - - for (Track track : movie.getTracks()) { - long tracksDuration = getDuration(track) * movieTimeScale / track.getTrackMetaData().getTimescale(); - if (tracksDuration > duration) { - duration = tracksDuration; - } - - - } - - mvhd.setDuration(duration); - mvhd.setTimescale(movieTimeScale); - // find the next available trackId - long nextTrackId = 0; - for (Track track : movie.getTracks()) { - nextTrackId = nextTrackId < track.getTrackMetaData().getTrackId() ? track.getTrackMetaData().getTrackId() : nextTrackId; - } - mvhd.setNextTrackId(++nextTrackId); - if (mvhd.getCreationTime() >= 1l << 32 || - mvhd.getModificationTime() >= 1l << 32 || - mvhd.getDuration() >= 1l << 32) { - mvhd.setVersion(1); - } - - movieBox.addBox(mvhd); - for (Track track : movie.getTracks()) { - movieBox.addBox(createTrackBox(track, movie)); - } - // metadata here - Box udta = createUdta(movie); - if (udta != null) { - movieBox.addBox(udta); - } - return movieBox; - - } - - /** - * Override to create a user data box that may contain metadata. - * - * @return a 'udta' box or <code>null</code> if none provided - */ - protected Box createUdta(Movie movie) { - return null; - } - - private TrackBox createTrackBox(Track track, Movie movie) { - - LOG.info("Creating Mp4TrackImpl " + track); - TrackBox trackBox = new TrackBox(); - TrackHeaderBox tkhd = new TrackHeaderBox(); - int flags = 0; - if (track.isEnabled()) { - flags += 1; - } - - if (track.isInMovie()) { - flags += 2; - } - - if (track.isInPreview()) { - flags += 4; - } - - if (track.isInPoster()) { - flags += 8; - } - tkhd.setFlags(flags); - - tkhd.setAlternateGroup(track.getTrackMetaData().getGroup()); - tkhd.setCreationTime(DateHelper.convert(track.getTrackMetaData().getCreationTime())); - // We need to take edit list box into account in trackheader duration - // but as long as I don't support edit list boxes it is sufficient to - // just translate media duration to movie timescale - tkhd.setDuration(getDuration(track) * getTimescale(movie) / track.getTrackMetaData().getTimescale()); - tkhd.setHeight(track.getTrackMetaData().getHeight()); - tkhd.setWidth(track.getTrackMetaData().getWidth()); - tkhd.setLayer(track.getTrackMetaData().getLayer()); - tkhd.setModificationTime(DateHelper.convert(new Date())); - tkhd.setTrackId(track.getTrackMetaData().getTrackId()); - tkhd.setVolume(track.getTrackMetaData().getVolume()); - if (tkhd.getCreationTime() >= 1l << 32 || - tkhd.getModificationTime() >= 1l << 32 || - tkhd.getDuration() >= 1l << 32) { - tkhd.setVersion(1); - } - - trackBox.addBox(tkhd); - -/* - EditBox edit = new EditBox(); - EditListBox editListBox = new EditListBox(); - editListBox.setEntries(Collections.singletonList( - new EditListBox.Entry(editListBox, (long) (track.getTrackMetaData().getStartTime() * getTimescale(movie)), -1, 1))); - edit.addBox(editListBox); - trackBox.addBox(edit); -*/ - - MediaBox mdia = new MediaBox(); - trackBox.addBox(mdia); - MediaHeaderBox mdhd = new MediaHeaderBox(); - mdhd.setCreationTime(DateHelper.convert(track.getTrackMetaData().getCreationTime())); - mdhd.setDuration(getDuration(track)); - mdhd.setTimescale(track.getTrackMetaData().getTimescale()); - mdhd.setLanguage(track.getTrackMetaData().getLanguage()); - mdia.addBox(mdhd); - HandlerBox hdlr = new HandlerBox(); - mdia.addBox(hdlr); - - hdlr.setHandlerType(track.getHandler()); - - MediaInformationBox minf = new MediaInformationBox(); - minf.addBox(track.getMediaHeaderBox()); - - // dinf: all these three boxes tell us is that the actual - // data is in the current file and not somewhere external - DataInformationBox dinf = new DataInformationBox(); - DataReferenceBox dref = new DataReferenceBox(); - dinf.addBox(dref); - DataEntryUrlBox url = new DataEntryUrlBox(); - url.setFlags(1); - dref.addBox(url); - minf.addBox(dinf); - // - - SampleTableBox stbl = new SampleTableBox(); - - stbl.addBox(track.getSampleDescriptionBox()); - - List<TimeToSampleBox.Entry> decodingTimeToSampleEntries = track.getDecodingTimeEntries(); - if (decodingTimeToSampleEntries != null && !track.getDecodingTimeEntries().isEmpty()) { - TimeToSampleBox stts = new TimeToSampleBox(); - stts.setEntries(track.getDecodingTimeEntries()); - stbl.addBox(stts); - } - - List<CompositionTimeToSample.Entry> compositionTimeToSampleEntries = track.getCompositionTimeEntries(); - if (compositionTimeToSampleEntries != null && !compositionTimeToSampleEntries.isEmpty()) { - CompositionTimeToSample ctts = new CompositionTimeToSample(); - ctts.setEntries(compositionTimeToSampleEntries); - stbl.addBox(ctts); - } - - long[] syncSamples = track.getSyncSamples(); - if (syncSamples != null && syncSamples.length > 0) { - SyncSampleBox stss = new SyncSampleBox(); - stss.setSampleNumber(syncSamples); - stbl.addBox(stss); - } - - if (track.getSampleDependencies() != null && !track.getSampleDependencies().isEmpty()) { - SampleDependencyTypeBox sdtp = new SampleDependencyTypeBox(); - sdtp.setEntries(track.getSampleDependencies()); - stbl.addBox(sdtp); - } - HashMap<Track, int[]> track2ChunkSizes = new HashMap<Track, int[]>(); - for (Track current : movie.getTracks()) { - track2ChunkSizes.put(current, getChunkSizes(current, movie)); - } - int[] tracksChunkSizes = track2ChunkSizes.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); - - SampleSizeBox stsz = new SampleSizeBox(); - stsz.setSampleSizes(track2SampleSizes.get(track)); - - stbl.addBox(stsz); - // 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. - StaticChunkOffsetBox stco = new StaticChunkOffsetBox(); - this.chunkOffsetBoxes.add(stco); - long offset = 0; - long[] chunkOffset = new long[tracksChunkSizes.length]; - // all tracks have the same number of chunks - if (LOG.isLoggable(Level.FINE)) { - LOG.fine("Calculating chunk offsets for track_" + track.getTrackMetaData().getTrackId()); - } - - - for (int i = 0; i < tracksChunkSizes.length; i++) { - // The filelayout will be: - // chunk_1_track_1,... ,chunk_1_track_n, chunk_2_track_1,... ,chunk_2_track_n, ... , chunk_m_track_1,... ,chunk_m_track_n - // calculating the offsets - if (LOG.isLoggable(Level.FINER)) { - LOG.finer("Calculating chunk offsets for track_" + track.getTrackMetaData().getTrackId() + " chunk " + i); - } - for (Track current : movie.getTracks()) { - if (LOG.isLoggable(Level.FINEST)) { - LOG.finest("Adding offsets of track_" + current.getTrackMetaData().getTrackId()); - } - int[] chunkSizes = track2ChunkSizes.get(current); - long firstSampleOfChunk = 0; - for (int j = 0; j < i; j++) { - firstSampleOfChunk += chunkSizes[j]; - } - if (current == track) { - chunkOffset[i] = offset; - } - for (int j = l2i(firstSampleOfChunk); j < firstSampleOfChunk + chunkSizes[i]; j++) { - offset += track2SampleSizes.get(current)[j]; - } - } - } - stco.setChunkOffsets(chunkOffset); - stbl.addBox(stco); - minf.addBox(stbl); - mdia.addBox(minf); - - return trackBox; - } - - private class InterleaveChunkMdat implements Box { - List<Track> tracks; - List<ByteBuffer> samples = new ArrayList<ByteBuffer>(); - ContainerBox parent; - - long contentSize = 0; - - public ContainerBox getParent() { - return parent; - } - - public void setParent(ContainerBox parent) { - this.parent = parent; - } - - public void parse(ReadableByteChannel readableByteChannel, ByteBuffer header, long contentSize, BoxParser boxParser) throws IOException { - } - - private InterleaveChunkMdat(Movie movie) { - - tracks = movie.getTracks(); - Map<Track, int[]> chunks = new HashMap<Track, int[]>(); - for (Track track : movie.getTracks()) { - chunks.put(track, getChunkSizes(track, movie)); - } - - for (int i = 0; i < chunks.values().iterator().next().length; i++) { - for (Track track : tracks) { - - int[] chunkSizes = chunks.get(track); - long firstSampleOfChunk = 0; - for (int j = 0; j < i; j++) { - firstSampleOfChunk += chunkSizes[j]; - } - - for (int j = l2i(firstSampleOfChunk); j < firstSampleOfChunk + chunkSizes[i]; j++) { - - ByteBuffer s = DefaultMp4Builder.this.track2Sample.get(track).get(j); - contentSize += s.limit(); - samples.add((ByteBuffer) s.rewind()); - } - - } - - } - - } - - public long getDataOffset() { - Box b = this; - long offset = 16; - while (b.getParent() != null) { - for (Box box : b.getParent().getBoxes()) { - if (b == box) { - break; - } - offset += box.getSize(); - } - b = b.getParent(); - } - return offset; - } - - - public String getType() { - return "mdat"; - } - - public long getSize() { - return 16 + contentSize; - } - - private boolean isSmallBox(long contentSize) { - return (contentSize + 8) < 4294967296L; - } - - - public void getBox(WritableByteChannel writableByteChannel) throws IOException { - ByteBuffer bb = ByteBuffer.allocate(16); - long size = getSize(); - if (isSmallBox(size)) { - IsoTypeWriter.writeUInt32(bb, size); - } else { - IsoTypeWriter.writeUInt32(bb, 1); - } - bb.put(IsoFile.fourCCtoBytes("mdat")); - if (isSmallBox(size)) { - bb.put(new byte[8]); - } else { - IsoTypeWriter.writeUInt64(bb, size); - } - bb.rewind(); - writableByteChannel.write(bb); - if (writableByteChannel instanceof GatheringByteChannel) { - List<ByteBuffer> nuSamples = unifyAdjacentBuffers(samples); - - - for (int i = 0; i < Math.ceil((double) nuSamples.size() / STEPSIZE); i++) { - List<ByteBuffer> sublist = nuSamples.subList( - i * STEPSIZE, // start - (i + 1) * STEPSIZE < nuSamples.size() ? (i + 1) * STEPSIZE : nuSamples.size()); // end - ByteBuffer sampleArray[] = sublist.toArray(new ByteBuffer[sublist.size()]); - do { - ((GatheringByteChannel) writableByteChannel).write(sampleArray); - } while (sampleArray[sampleArray.length - 1].remaining() > 0); - } - //System.err.println(bytesWritten); - } else { - for (ByteBuffer sample : samples) { - sample.rewind(); - writableByteChannel.write(sample); - } - } - } - - } - - /** - * Gets the chunk sizes for the given track. - * - * @param track - * @param movie - * @return - */ - int[] getChunkSizes(Track track, Movie movie) { - - long[] referenceChunkStarts = intersectionFinder.sampleNumbers(track, movie); - int[] chunkSizes = new int[referenceChunkStarts.length]; - - - for (int i = 0; i < referenceChunkStarts.length; i++) { - long start = referenceChunkStarts[i] - 1; - long end; - if (referenceChunkStarts.length == i + 1) { - end = track.getSamples().size(); - } else { - end = referenceChunkStarts[i + 1] - 1; - } - - chunkSizes[i] = l2i(end - start); - // The Stretch makes sure that there are as much audio and video chunks! - } - assert DefaultMp4Builder.this.track2Sample.get(track).size() == sum(chunkSizes) : "The number of samples and the sum of all chunk lengths must be equal"; - return chunkSizes; - - - } - - - private static long sum(int[] ls) { - long rc = 0; - for (long l : ls) { - rc += l; - } - return rc; - } - - protected static long getDuration(Track track) { - long duration = 0; - for (TimeToSampleBox.Entry entry : track.getDecodingTimeEntries()) { - duration += entry.getCount() * entry.getDelta(); - } - return duration; - } - - public long getTimescale(Movie movie) { - long timescale = movie.getTracks().iterator().next().getTrackMetaData().getTimescale(); - for (Track track : movie.getTracks()) { - timescale = gcd(track.getTrackMetaData().getTimescale(), timescale); - } - return timescale; - } - - public static long gcd(long a, long b) { - if (b == 0) { - return a; - } - return gcd(b, a % b); - } - - public List<ByteBuffer> unifyAdjacentBuffers(List<ByteBuffer> samples) { - ArrayList<ByteBuffer> nuSamples = new ArrayList<ByteBuffer>(samples.size()); - for (ByteBuffer buffer : samples) { - int lastIndex = nuSamples.size() - 1; - if (lastIndex >= 0 && buffer.hasArray() && nuSamples.get(lastIndex).hasArray() && buffer.array() == nuSamples.get(lastIndex).array() && - nuSamples.get(lastIndex).arrayOffset() + nuSamples.get(lastIndex).limit() == buffer.arrayOffset()) { - ByteBuffer oldBuffer = nuSamples.remove(lastIndex); - ByteBuffer nu = ByteBuffer.wrap(buffer.array(), oldBuffer.arrayOffset(), oldBuffer.limit() + buffer.limit()).slice(); - // We need to slice here since wrap([], offset, length) just sets position and not the arrayOffset. - nuSamples.add(nu); - } else if (lastIndex >= 0 && - buffer instanceof MappedByteBuffer && nuSamples.get(lastIndex) instanceof MappedByteBuffer && - nuSamples.get(lastIndex).limit() == nuSamples.get(lastIndex).capacity() - buffer.capacity()) { - // This can go wrong - but will it? - ByteBuffer oldBuffer = nuSamples.get(lastIndex); - oldBuffer.limit(buffer.limit() + oldBuffer.limit()); - } else { - nuSamples.add(buffer); - } - } - return nuSamples; - } -} diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/.svn/text-base/FragmentIntersectionFinder.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/.svn/text-base/FragmentIntersectionFinder.java.svn-base deleted file mode 100644 index 1224bbf..0000000 --- a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/.svn/text-base/FragmentIntersectionFinder.java.svn-base +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2012 Sebastian Annies, Hamburg - * - * Licensed under the Apache License, Version 2.0 (the License); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an AS IS BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.googlecode.mp4parser.authoring.builder; - -import com.googlecode.mp4parser.authoring.Movie; -import com.googlecode.mp4parser.authoring.Track; - -/** - * - */ -public interface FragmentIntersectionFinder { - /** - * Gets the ordinal number of the samples which will be the first sample - * in each fragment. - * - * @param track concerned track - * @param movie the context of the track - * @return an array containing the ordinal of each fragment's first sample - */ - public long[] sampleNumbers(Track track, Movie movie); -} diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/.svn/text-base/FragmentedMp4Builder.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/.svn/text-base/FragmentedMp4Builder.java.svn-base deleted file mode 100644 index c65ff1c..0000000 --- a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/.svn/text-base/FragmentedMp4Builder.java.svn-base +++ /dev/null @@ -1,742 +0,0 @@ -/* - * Copyright 2012 Sebastian Annies, Hamburg - * - * Licensed under the Apache License, Version 2.0 (the License); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an AS IS BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.googlecode.mp4parser.authoring.builder; - -import com.coremedia.iso.BoxParser; -import com.coremedia.iso.IsoFile; -import com.coremedia.iso.IsoTypeWriter; -import com.coremedia.iso.boxes.*; -import com.coremedia.iso.boxes.fragment.*; -import com.googlecode.mp4parser.authoring.DateHelper; -import com.googlecode.mp4parser.authoring.Movie; -import com.googlecode.mp4parser.authoring.Track; - -import java.io.IOException; -import java.nio.ByteBuffer; -import java.nio.channels.GatheringByteChannel; -import java.nio.channels.ReadableByteChannel; -import java.nio.channels.WritableByteChannel; -import java.util.*; -import java.util.logging.Logger; - -import static com.googlecode.mp4parser.util.CastUtils.l2i; - -/** - * Creates a fragmented MP4 file. - */ -public class FragmentedMp4Builder implements Mp4Builder { - private static final Logger LOG = Logger.getLogger(FragmentedMp4Builder.class.getName()); - - protected FragmentIntersectionFinder intersectionFinder; - - public FragmentedMp4Builder() { - this.intersectionFinder = new SyncSampleIntersectFinderImpl(); - } - - public List<String> getAllowedHandlers() { - return Arrays.asList("soun", "vide"); - } - - public Box createFtyp(Movie movie) { - List<String> minorBrands = new LinkedList<String>(); - minorBrands.add("isom"); - minorBrands.add("iso2"); - minorBrands.add("avc1"); - return new FileTypeBox("isom", 0, minorBrands); - } - - /** - * Some formats require sorting of the fragments. E.g. Ultraviolet CFF files are required - * to contain the fragments size sort: - * <ul> - * <li>video[1].getBytes().length < audio[1].getBytes().length < subs[1].getBytes().length</li> - * <li> audio[2].getBytes().length < video[2].getBytes().length < subs[2].getBytes().length</li> - * </ul> - * - * make this fragment: - * - * <ol> - * <li>video[1]</li> - * <li>audio[1]</li> - * <li>subs[1]</li> - * <li>audio[2]</li> - * <li>video[2]</li> - * <li>subs[2]</li> - * </ol> - * - * @param tracks the list of tracks to returned sorted - * @param cycle current fragment (sorting may vary between the fragments) - * @param intersectionMap a map from tracks to their fragments' first samples. - * @return the list of tracks in order of appearance in the fragment - */ - protected List<Track> sortTracksInSequence(List<Track> tracks, final int cycle, final Map<Track, long[]> intersectionMap) { - tracks = new LinkedList<Track>(tracks); - Collections.sort(tracks, new Comparator<Track>() { - public int compare(Track o1, Track o2) { - long[] startSamples1 = intersectionMap.get(o1); - long startSample1 = startSamples1[cycle]; - // one based sample numbers - the first sample is 1 - long endSample1 = cycle + 1 < startSamples1.length ? startSamples1[cycle + 1] : o1.getSamples().size() + 1; - long[] startSamples2 = intersectionMap.get(o2); - long startSample2 = startSamples2[cycle]; - // one based sample numbers - the first sample is 1 - long endSample2 = cycle + 1 < startSamples2.length ? startSamples2[cycle + 1] : o2.getSamples().size() + 1; - List<ByteBuffer> samples1 = o1.getSamples().subList(l2i(startSample1) - 1, l2i(endSample1) - 1); - List<ByteBuffer> samples2 = o2.getSamples().subList(l2i(startSample2) - 1, l2i(endSample2) - 1); - int size1 = 0; - for (ByteBuffer byteBuffer : samples1) { - size1 += byteBuffer.limit(); - } - int size2 = 0; - for (ByteBuffer byteBuffer : samples2) { - size2 += byteBuffer.limit(); - } - return size1 - size2; - } - }); - return tracks; - } - - protected List<Box> createMoofMdat(final Movie movie) { - List<Box> boxes = new LinkedList<Box>(); - HashMap<Track, long[]> intersectionMap = new HashMap<Track, long[]>(); - int maxNumberOfFragments = 0; - for (Track track : movie.getTracks()) { - long[] intersects = intersectionFinder.sampleNumbers(track, movie); - intersectionMap.put(track, intersects); - maxNumberOfFragments = Math.max(maxNumberOfFragments, intersects.length); - } - - - int sequence = 1; - // this loop has two indices: - - for (int cycle = 0; cycle < maxNumberOfFragments; cycle++) { - - final List<Track> sortedTracks = sortTracksInSequence(movie.getTracks(), cycle, intersectionMap); - - for (Track track : sortedTracks) { - if (getAllowedHandlers().isEmpty() || getAllowedHandlers().contains(track.getHandler())) { - long[] startSamples = intersectionMap.get(track); - //some tracks may have less fragments -> skip them - if (cycle < startSamples.length) { - - long startSample = startSamples[cycle]; - // one based sample numbers - the first sample is 1 - long endSample = cycle + 1 < startSamples.length ? startSamples[cycle + 1] : track.getSamples().size() + 1; - - // if startSample == endSample the cycle is empty! - if (startSample != endSample) { - boxes.add(createMoof(startSample, endSample, track, sequence)); - boxes.add(createMdat(startSample, endSample, track, sequence++)); - } - } - } - } - } - return boxes; - } - - /** - * {@inheritDoc} - */ - public IsoFile build(Movie movie) { - LOG.fine("Creating movie " + movie); - IsoFile isoFile = new IsoFile(); - - - isoFile.addBox(createFtyp(movie)); - isoFile.addBox(createMoov(movie)); - - for (Box box : createMoofMdat(movie)) { - isoFile.addBox(box); - } - isoFile.addBox(createMfra(movie, isoFile)); - - return isoFile; - } - - protected Box createMdat(final long startSample, final long endSample, final Track track, final int i) { - - class Mdat implements Box { - ContainerBox parent; - - public ContainerBox getParent() { - return parent; - } - - public void setParent(ContainerBox parent) { - this.parent = parent; - } - - public long getSize() { - long size = 8; // I don't expect 2gig fragments - for (ByteBuffer sample : getSamples(startSample, endSample, track, i)) { - size += sample.limit(); - } - return size; - } - - public String getType() { - return "mdat"; - } - - public void getBox(WritableByteChannel writableByteChannel) throws IOException { - List<ByteBuffer> bbs = getSamples(startSample, endSample, track, i); - final List<ByteBuffer> samples = ByteBufferHelper.mergeAdjacentBuffers(bbs); - ByteBuffer header = ByteBuffer.allocate(8); - IsoTypeWriter.writeUInt32(header, l2i(getSize())); - header.put(IsoFile.fourCCtoBytes(getType())); - header.rewind(); - writableByteChannel.write(header); - if (writableByteChannel instanceof GatheringByteChannel) { - - int STEPSIZE = 1024; - // This is required to prevent android from crashing - // it seems that {@link GatheringByteChannel#write(java.nio.ByteBuffer[])} - // just handles up to 1024 buffers - for (int i = 0; i < Math.ceil((double) samples.size() / STEPSIZE); i++) { - List<ByteBuffer> sublist = samples.subList( - i * STEPSIZE, // start - (i + 1) * STEPSIZE < samples.size() ? (i + 1) * STEPSIZE : samples.size()); // end - ByteBuffer sampleArray[] = sublist.toArray(new ByteBuffer[sublist.size()]); - do { - ((GatheringByteChannel) writableByteChannel).write(sampleArray); - } while (sampleArray[sampleArray.length - 1].remaining() > 0); - } - //System.err.println(bytesWritten); - } else { - for (ByteBuffer sample : samples) { - sample.rewind(); - writableByteChannel.write(sample); - } - } - - } - - public void parse(ReadableByteChannel readableByteChannel, ByteBuffer header, long contentSize, BoxParser boxParser) throws IOException { - - } - } - - return new Mdat(); - } - - protected Box createTfhd(long startSample, long endSample, Track track, int sequenceNumber) { - TrackFragmentHeaderBox tfhd = new TrackFragmentHeaderBox(); - SampleFlags sf = new SampleFlags(); - - tfhd.setDefaultSampleFlags(sf); - tfhd.setBaseDataOffset(-1); - tfhd.setTrackId(track.getTrackMetaData().getTrackId()); - return tfhd; - } - - protected Box createMfhd(long startSample, long endSample, Track track, int sequenceNumber) { - MovieFragmentHeaderBox mfhd = new MovieFragmentHeaderBox(); - mfhd.setSequenceNumber(sequenceNumber); - return mfhd; - } - - protected Box createTraf(long startSample, long endSample, Track track, int sequenceNumber) { - TrackFragmentBox traf = new TrackFragmentBox(); - traf.addBox(createTfhd(startSample, endSample, track, sequenceNumber)); - for (Box trun : createTruns(startSample, endSample, track, sequenceNumber)) { - traf.addBox(trun); - } - - return traf; - } - - - /** - * Gets the all samples starting with <code>startSample</code> (one based -> one is the first) and - * ending with <code>endSample</code> (exclusive). - * - * @param startSample low endpoint (inclusive) of the sample sequence - * @param endSample high endpoint (exclusive) of the sample sequence - * @param track source of the samples - * @param sequenceNumber the fragment index of the requested list of samples - * @return a <code>List<ByteBuffer></code> of raw samples - */ - protected List<ByteBuffer> getSamples(long startSample, long endSample, Track track, int sequenceNumber) { - // since startSample and endSample are one-based substract 1 before addressing list elements - return track.getSamples().subList(l2i(startSample) - 1, l2i(endSample) - 1); - } - - /** - * Gets the sizes of a sequence of samples- - * - * @param startSample low endpoint (inclusive) of the sample sequence - * @param endSample high endpoint (exclusive) of the sample sequence - * @param track source of the samples - * @param sequenceNumber the fragment index of the requested list of samples - * @return - */ - protected long[] getSampleSizes(long startSample, long endSample, Track track, int sequenceNumber) { - List<ByteBuffer> samples = getSamples(startSample, endSample, track, sequenceNumber); - - long[] sampleSizes = new long[samples.size()]; - for (int i = 0; i < sampleSizes.length; i++) { - sampleSizes[i] = samples.get(i).limit(); - } - return sampleSizes; - } - - /** - * Creates one or more track run boxes for a given sequence. - * - * @param startSample low endpoint (inclusive) of the sample sequence - * @param endSample high endpoint (exclusive) of the sample sequence - * @param track source of the samples - * @param sequenceNumber the fragment index of the requested list of samples - * @return the list of TrackRun boxes. - */ - protected List<? extends Box> createTruns(long startSample, long endSample, Track track, int sequenceNumber) { - TrackRunBox trun = new TrackRunBox(); - long[] sampleSizes = getSampleSizes(startSample, endSample, track, sequenceNumber); - - trun.setSampleDurationPresent(true); - trun.setSampleSizePresent(true); - List<TrackRunBox.Entry> entries = new ArrayList<TrackRunBox.Entry>(l2i(endSample - startSample)); - - - Queue<TimeToSampleBox.Entry> timeQueue = new LinkedList<TimeToSampleBox.Entry>(track.getDecodingTimeEntries()); - long left = startSample - 1; - long curEntryLeft = timeQueue.peek().getCount(); - while (left > curEntryLeft) { - left -= curEntryLeft; - timeQueue.remove(); - curEntryLeft = timeQueue.peek().getCount(); - } - curEntryLeft -= left; - - - Queue<CompositionTimeToSample.Entry> compositionTimeQueue = - track.getCompositionTimeEntries() != null && track.getCompositionTimeEntries().size() > 0 ? - new LinkedList<CompositionTimeToSample.Entry>(track.getCompositionTimeEntries()) : null; - long compositionTimeEntriesLeft = compositionTimeQueue != null ? compositionTimeQueue.peek().getCount() : -1; - - - trun.setSampleCompositionTimeOffsetPresent(compositionTimeEntriesLeft > 0); - - // fast forward composition stuff - for (long i = 1; i < startSample; i++) { - if (compositionTimeQueue != null) { - //trun.setSampleCompositionTimeOffsetPresent(true); - if (--compositionTimeEntriesLeft == 0 && compositionTimeQueue.size() > 1) { - compositionTimeQueue.remove(); - compositionTimeEntriesLeft = compositionTimeQueue.element().getCount(); - } - } - } - - boolean sampleFlagsRequired = (track.getSampleDependencies() != null && !track.getSampleDependencies().isEmpty() || - track.getSyncSamples() != null && track.getSyncSamples().length != 0); - - trun.setSampleFlagsPresent(sampleFlagsRequired); - - for (int i = 0; i < sampleSizes.length; i++) { - TrackRunBox.Entry entry = new TrackRunBox.Entry(); - entry.setSampleSize(sampleSizes[i]); - if (sampleFlagsRequired) { - //if (false) { - SampleFlags sflags = new SampleFlags(); - - if (track.getSampleDependencies() != null && !track.getSampleDependencies().isEmpty()) { - SampleDependencyTypeBox.Entry e = track.getSampleDependencies().get(i); - sflags.setSampleDependsOn(e.getSampleDependsOn()); - sflags.setSampleIsDependedOn(e.getSampleIsDependentOn()); - sflags.setSampleHasRedundancy(e.getSampleHasRedundancy()); - } - if (track.getSyncSamples() != null && track.getSyncSamples().length > 0) { - // we have to mark non-sync samples! - if (Arrays.binarySearch(track.getSyncSamples(), startSample + i) >= 0) { - sflags.setSampleIsDifferenceSample(false); - sflags.setSampleDependsOn(2); - } else { - sflags.setSampleIsDifferenceSample(true); - sflags.setSampleDependsOn(1); - } - } - // i don't have sample degradation - entry.setSampleFlags(sflags); - - } - - entry.setSampleDuration(timeQueue.peek().getDelta()); - if (--curEntryLeft == 0 && timeQueue.size() > 1) { - timeQueue.remove(); - curEntryLeft = timeQueue.peek().getCount(); - } - - if (compositionTimeQueue != null) { - entry.setSampleCompositionTimeOffset(compositionTimeQueue.peek().getOffset()); - if (--compositionTimeEntriesLeft == 0 && compositionTimeQueue.size() > 1) { - compositionTimeQueue.remove(); - compositionTimeEntriesLeft = compositionTimeQueue.element().getCount(); - } - } - entries.add(entry); - } - - trun.setEntries(entries); - - return Collections.singletonList(trun); - } - - /** - * Creates a 'moof' box for a given sequence of samples. - * - * @param startSample low endpoint (inclusive) of the sample sequence - * @param endSample high endpoint (exclusive) of the sample sequence - * @param track source of the samples - * @param sequenceNumber the fragment index of the requested list of samples - * @return the list of TrackRun boxes. - */ - protected Box createMoof(long startSample, long endSample, Track track, int sequenceNumber) { - MovieFragmentBox moof = new MovieFragmentBox(); - moof.addBox(createMfhd(startSample, endSample, track, sequenceNumber)); - moof.addBox(createTraf(startSample, endSample, track, sequenceNumber)); - - TrackRunBox firstTrun = moof.getTrackRunBoxes().get(0); - firstTrun.setDataOffset(1); // dummy to make size correct - firstTrun.setDataOffset((int) (8 + moof.getSize())); // mdat header + moof size - - return moof; - } - - /** - * Creates a single 'mvhd' movie header box for a given movie. - * - * @param movie the concerned movie - * @return an 'mvhd' box - */ - protected Box createMvhd(Movie movie) { - MovieHeaderBox mvhd = new MovieHeaderBox(); - mvhd.setVersion(1); - mvhd.setCreationTime(DateHelper.convert(new Date())); - mvhd.setModificationTime(DateHelper.convert(new Date())); - long movieTimeScale = movie.getTimescale(); - long duration = 0; - - for (Track track : movie.getTracks()) { - long tracksDuration = getDuration(track) * movieTimeScale / track.getTrackMetaData().getTimescale(); - if (tracksDuration > duration) { - duration = tracksDuration; - } - - - } - - mvhd.setDuration(duration); - mvhd.setTimescale(movieTimeScale); - // find the next available trackId - long nextTrackId = 0; - for (Track track : movie.getTracks()) { - nextTrackId = nextTrackId < track.getTrackMetaData().getTrackId() ? track.getTrackMetaData().getTrackId() : nextTrackId; - } - mvhd.setNextTrackId(++nextTrackId); - return mvhd; - } - - /** - * Creates a fully populated 'moov' box with all child boxes. Child boxes are: - * <ul> - * <li>{@link #createMvhd(com.googlecode.mp4parser.authoring.Movie) mvhd}</li> - * <li>{@link #createMvex(com.googlecode.mp4parser.authoring.Movie) mvex}</li> - * <li>a {@link #createTrak(com.googlecode.mp4parser.authoring.Track, com.googlecode.mp4parser.authoring.Movie) trak} for every track</li> - * </ul> - * - * @param movie the concerned movie - * @return fully populated 'moov' - */ - protected Box createMoov(Movie movie) { - MovieBox movieBox = new MovieBox(); - - movieBox.addBox(createMvhd(movie)); - movieBox.addBox(createMvex(movie)); - - for (Track track : movie.getTracks()) { - movieBox.addBox(createTrak(track, movie)); - } - // metadata here - return movieBox; - - } - - /** - * Creates a 'tfra' - track fragment random access box for the given track with the isoFile. - * The tfra contains a map of random access points with time as key and offset within the isofile - * as value. - * - * @param track the concerned track - * @param isoFile the track is contained in - * @return a track fragment random access box. - */ - protected Box createTfra(Track track, IsoFile isoFile) { - TrackFragmentRandomAccessBox tfra = new TrackFragmentRandomAccessBox(); - tfra.setVersion(1); // use long offsets and times - List<TrackFragmentRandomAccessBox.Entry> offset2timeEntries = new LinkedList<TrackFragmentRandomAccessBox.Entry>(); - List<Box> boxes = isoFile.getBoxes(); - long offset = 0; - long duration = 0; - for (Box box : boxes) { - if (box instanceof MovieFragmentBox) { - List<TrackFragmentBox> trafs = ((MovieFragmentBox) box).getBoxes(TrackFragmentBox.class); - for (int i = 0; i < trafs.size(); i++) { - TrackFragmentBox traf = trafs.get(i); - if (traf.getTrackFragmentHeaderBox().getTrackId() == track.getTrackMetaData().getTrackId()) { - // here we are at the offset required for the current entry. - List<TrackRunBox> truns = traf.getBoxes(TrackRunBox.class); - for (int j = 0; j < truns.size(); j++) { - List<TrackFragmentRandomAccessBox.Entry> offset2timeEntriesThisTrun = new LinkedList<TrackFragmentRandomAccessBox.Entry>(); - TrackRunBox trun = truns.get(j); - for (int k = 0; k < trun.getEntries().size(); k++) { - TrackRunBox.Entry trunEntry = trun.getEntries().get(k); - SampleFlags sf = null; - if (k == 0 && trun.isFirstSampleFlagsPresent()) { - sf = trun.getFirstSampleFlags(); - } else if (trun.isSampleFlagsPresent()) { - sf = trunEntry.getSampleFlags(); - } else { - List<MovieExtendsBox> mvexs = isoFile.getMovieBox().getBoxes(MovieExtendsBox.class); - for (MovieExtendsBox mvex : mvexs) { - List<TrackExtendsBox> trexs = mvex.getBoxes(TrackExtendsBox.class); - for (TrackExtendsBox trex : trexs) { - if (trex.getTrackId() == track.getTrackMetaData().getTrackId()) { - sf = trex.getDefaultSampleFlags(); - } - } - } - - } - if (sf == null) { - throw new RuntimeException("Could not find any SampleFlags to indicate random access or not"); - } - if (sf.getSampleDependsOn() == 2) { - offset2timeEntriesThisTrun.add(new TrackFragmentRandomAccessBox.Entry( - duration, - offset, - i + 1, j + 1, k + 1)); - } - duration += trunEntry.getSampleDuration(); - } - if (offset2timeEntriesThisTrun.size() == trun.getEntries().size() && trun.getEntries().size() > 0) { - // Oooops every sample seems to be random access sample - // is this an audio track? I don't care. - // I just use the first for trun sample for tfra random access - offset2timeEntries.add(offset2timeEntriesThisTrun.get(0)); - } else { - offset2timeEntries.addAll(offset2timeEntriesThisTrun); - } - } - } - } - } - - - offset += box.getSize(); - } - tfra.setEntries(offset2timeEntries); - tfra.setTrackId(track.getTrackMetaData().getTrackId()); - return tfra; - } - - /** - * Creates a 'mfra' - movie fragment random access box for the given movie in the given - * isofile. Uses {@link #createTfra(com.googlecode.mp4parser.authoring.Track, com.coremedia.iso.IsoFile)} - * to generate the child boxes. - * - * @param movie concerned movie - * @param isoFile concerned isofile - * @return a complete 'mfra' box - */ - protected Box createMfra(Movie movie, IsoFile isoFile) { - MovieFragmentRandomAccessBox mfra = new MovieFragmentRandomAccessBox(); - for (Track track : movie.getTracks()) { - mfra.addBox(createTfra(track, isoFile)); - } - - MovieFragmentRandomAccessOffsetBox mfro = new MovieFragmentRandomAccessOffsetBox(); - mfra.addBox(mfro); - mfro.setMfraSize(mfra.getSize()); - return mfra; - } - - protected Box createTrex(Movie movie, Track track) { - TrackExtendsBox trex = new TrackExtendsBox(); - trex.setTrackId(track.getTrackMetaData().getTrackId()); - trex.setDefaultSampleDescriptionIndex(1); - trex.setDefaultSampleDuration(0); - trex.setDefaultSampleSize(0); - SampleFlags sf = new SampleFlags(); - if ("soun".equals(track.getHandler())) { - // as far as I know there is no audio encoding - // where the sample are not self contained. - sf.setSampleDependsOn(2); - sf.setSampleIsDependedOn(2); - } - trex.setDefaultSampleFlags(sf); - return trex; - } - - /** - * Creates a 'mvex' - movie extends box and populates it with 'trex' boxes - * by calling {@link #createTrex(com.googlecode.mp4parser.authoring.Movie, com.googlecode.mp4parser.authoring.Track)} - * for each track to generate them - * - * @param movie the source movie - * @return a complete 'mvex' - */ - protected Box createMvex(Movie movie) { - MovieExtendsBox mvex = new MovieExtendsBox(); - final MovieExtendsHeaderBox mved = new MovieExtendsHeaderBox(); - for (Track track : movie.getTracks()) { - final long trackDuration = getTrackDuration(movie, track); - if (mved.getFragmentDuration() < trackDuration) { - mved.setFragmentDuration(trackDuration); - } - } - mvex.addBox(mved); - - for (Track track : movie.getTracks()) { - mvex.addBox(createTrex(movie, track)); - } - return mvex; - } - - protected Box createTkhd(Movie movie, Track track) { - TrackHeaderBox tkhd = new TrackHeaderBox(); - tkhd.setVersion(1); - int flags = 0; - if (track.isEnabled()) { - flags += 1; - } - - if (track.isInMovie()) { - flags += 2; - } - - if (track.isInPreview()) { - flags += 4; - } - - if (track.isInPoster()) { - flags += 8; - } - tkhd.setFlags(flags); - - tkhd.setAlternateGroup(track.getTrackMetaData().getGroup()); - tkhd.setCreationTime(DateHelper.convert(track.getTrackMetaData().getCreationTime())); - // We need to take edit list box into account in trackheader duration - // but as long as I don't support edit list boxes it is sufficient to - // just translate media duration to movie timescale - tkhd.setDuration(getTrackDuration(movie, track)); - tkhd.setHeight(track.getTrackMetaData().getHeight()); - tkhd.setWidth(track.getTrackMetaData().getWidth()); - tkhd.setLayer(track.getTrackMetaData().getLayer()); - tkhd.setModificationTime(DateHelper.convert(new Date())); - tkhd.setTrackId(track.getTrackMetaData().getTrackId()); - tkhd.setVolume(track.getTrackMetaData().getVolume()); - return tkhd; - } - - private long getTrackDuration(Movie movie, Track track) { - return getDuration(track) * movie.getTimescale() / track.getTrackMetaData().getTimescale(); - } - - protected Box createMdhd(Movie movie, Track track) { - MediaHeaderBox mdhd = new MediaHeaderBox(); - mdhd.setCreationTime(DateHelper.convert(track.getTrackMetaData().getCreationTime())); - mdhd.setDuration(getDuration(track)); - mdhd.setTimescale(track.getTrackMetaData().getTimescale()); - mdhd.setLanguage(track.getTrackMetaData().getLanguage()); - return mdhd; - } - - protected Box createStbl(Movie movie, Track track) { - SampleTableBox stbl = new SampleTableBox(); - - stbl.addBox(track.getSampleDescriptionBox()); - stbl.addBox(new TimeToSampleBox()); - //stbl.addBox(new SampleToChunkBox()); - stbl.addBox(new StaticChunkOffsetBox()); - return stbl; - } - - protected Box createMinf(Track track, Movie movie) { - MediaInformationBox minf = new MediaInformationBox(); - minf.addBox(track.getMediaHeaderBox()); - minf.addBox(createDinf(movie, track)); - minf.addBox(createStbl(movie, track)); - return minf; - } - - protected Box createMdiaHdlr(Track track, Movie movie) { - HandlerBox hdlr = new HandlerBox(); - hdlr.setHandlerType(track.getHandler()); - return hdlr; - } - - protected Box createMdia(Track track, Movie movie) { - MediaBox mdia = new MediaBox(); - mdia.addBox(createMdhd(movie, track)); - - - mdia.addBox(createMdiaHdlr(track, movie)); - - - mdia.addBox(createMinf(track, movie)); - return mdia; - } - - protected Box createTrak(Track track, Movie movie) { - LOG.fine("Creating Track " + track); - TrackBox trackBox = new TrackBox(); - trackBox.addBox(createTkhd(movie, track)); - trackBox.addBox(createMdia(track, movie)); - return trackBox; - } - - protected DataInformationBox createDinf(Movie movie, Track track) { - DataInformationBox dinf = new DataInformationBox(); - DataReferenceBox dref = new DataReferenceBox(); - dinf.addBox(dref); - DataEntryUrlBox url = new DataEntryUrlBox(); - url.setFlags(1); - dref.addBox(url); - return dinf; - } - - public FragmentIntersectionFinder getFragmentIntersectionFinder() { - return intersectionFinder; - } - - public void setIntersectionFinder(FragmentIntersectionFinder intersectionFinder) { - this.intersectionFinder = intersectionFinder; - } - - protected long getDuration(Track track) { - long duration = 0; - for (TimeToSampleBox.Entry entry : track.getDecodingTimeEntries()) { - duration += entry.getCount() * entry.getDelta(); - } - return duration; - } - - -} diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/.svn/text-base/Mp4Builder.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/.svn/text-base/Mp4Builder.java.svn-base deleted file mode 100644 index 725745e..0000000 --- a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/.svn/text-base/Mp4Builder.java.svn-base +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2012 Sebastian Annies, Hamburg - * - * Licensed under the Apache License, Version 2.0 (the License); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an AS IS BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.googlecode.mp4parser.authoring.builder; - -import com.coremedia.iso.IsoFile; -import com.googlecode.mp4parser.authoring.Movie; - -/** - * Transforms a <code>Movie</code> object to an IsoFile. Implementations can - * determine the specific format: Fragmented MP4, MP4, MP4 with Apple Metadata, - * MP4 with 3GPP Metadata, MOV. - */ -public interface Mp4Builder { - /** - * Builds the actual IsoFile from the Movie. - * - * @param movie data source - * @return the freshly built IsoFile - */ - public IsoFile build(Movie movie); - -} diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/.svn/text-base/SyncSampleIntersectFinderImpl.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/.svn/text-base/SyncSampleIntersectFinderImpl.java.svn-base deleted file mode 100644 index 2766c5e..0000000 --- a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/.svn/text-base/SyncSampleIntersectFinderImpl.java.svn-base +++ /dev/null @@ -1,334 +0,0 @@ -/* - * Copyright 2012 Sebastian Annies, Hamburg - * - * Licensed under the Apache License, Version 2.0 (the License); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an AS IS BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.googlecode.mp4parser.authoring.builder; - -import com.coremedia.iso.boxes.TimeToSampleBox; -import com.coremedia.iso.boxes.sampleentry.AudioSampleEntry; -import com.googlecode.mp4parser.authoring.Movie; -import com.googlecode.mp4parser.authoring.Track; - -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; -import java.util.logging.Logger; - -import static com.googlecode.mp4parser.util.Math.lcm; - -/** - * This <code>FragmentIntersectionFinder</code> cuts the input movie video tracks in - * fragments of the same length exactly before the sync samples. Audio tracks are cut - * into pieces of similar length. - */ -public class SyncSampleIntersectFinderImpl implements FragmentIntersectionFinder { - - private static Logger LOG = Logger.getLogger(SyncSampleIntersectFinderImpl.class.getName()); - private static Map<CacheTuple, long[]> getTimesCache = new ConcurrentHashMap<CacheTuple, long[]>(); - private static Map<CacheTuple, long[]> getSampleNumbersCache = new ConcurrentHashMap<CacheTuple, long[]>(); - - private final int minFragmentDurationSeconds; - - public SyncSampleIntersectFinderImpl() { - minFragmentDurationSeconds = 0; - } - - /** - * Creates a <code>SyncSampleIntersectFinderImpl</code> that will not create any fragment - * smaller than the given <code>minFragmentDurationSeconds</code> - * - * @param minFragmentDurationSeconds the smallest allowable duration of a fragment. - */ - public SyncSampleIntersectFinderImpl(int minFragmentDurationSeconds) { - this.minFragmentDurationSeconds = minFragmentDurationSeconds; - } - - /** - * Gets an array of sample numbers that are meant to be the first sample of each - * chunk or fragment. - * - * @param track concerned track - * @param movie the context of the track - * @return an array containing the ordinal of each fragment's first sample - */ - public long[] sampleNumbers(Track track, Movie movie) { - final CacheTuple key = new CacheTuple(track, movie); - final long[] result = getSampleNumbersCache.get(key); - if (result != null) { - return result; - } - - if ("vide".equals(track.getHandler())) { - if (track.getSyncSamples() != null && track.getSyncSamples().length > 0) { - List<long[]> times = getSyncSamplesTimestamps(movie, track); - final long[] commonIndices = getCommonIndices(track.getSyncSamples(), getTimes(track, movie), track.getTrackMetaData().getTimescale(), times.toArray(new long[times.size()][])); - getSampleNumbersCache.put(key, commonIndices); - return commonIndices; - } else { - throw new RuntimeException("Video Tracks need sync samples. Only tracks other than video may have no sync samples."); - } - } else if ("soun".equals(track.getHandler())) { - Track referenceTrack = null; - for (Track candidate : movie.getTracks()) { - if (candidate.getSyncSamples() != null && "vide".equals(candidate.getHandler()) && candidate.getSyncSamples().length > 0) { - referenceTrack = candidate; - } - } - if (referenceTrack != null) { - - // Gets the reference track's fra - long[] refSyncSamples = sampleNumbers(referenceTrack, movie); - - int refSampleCount = referenceTrack.getSamples().size(); - - long[] syncSamples = new long[refSyncSamples.length]; - long minSampleRate = 192000; - for (Track testTrack : movie.getTracks()) { - if ("soun".equals(testTrack.getHandler())) { - AudioSampleEntry ase = (AudioSampleEntry) testTrack.getSampleDescriptionBox().getSampleEntry(); - if (ase.getSampleRate() < minSampleRate) { - minSampleRate = ase.getSampleRate(); - long sc = testTrack.getSamples().size(); - double stretch = (double) sc / refSampleCount; - TimeToSampleBox.Entry sttsEntry = testTrack.getDecodingTimeEntries().get(0); - long samplesPerFrame = sttsEntry.getDelta(); // Assuming all audio tracks have the same number of samples per frame, which they do for all known types - - for (int i = 0; i < syncSamples.length; i++) { - long start = (long) Math.ceil(stretch * (refSyncSamples[i] - 1) * samplesPerFrame); - syncSamples[i] = start; - // The Stretch makes sure that there are as much audio and video chunks! - } - break; - } - } - } - AudioSampleEntry ase = (AudioSampleEntry) track.getSampleDescriptionBox().getSampleEntry(); - TimeToSampleBox.Entry sttsEntry = track.getDecodingTimeEntries().get(0); - long samplesPerFrame = sttsEntry.getDelta(); // Assuming all audio tracks have the same number of samples per frame, which they do for all known types - double factor = (double) ase.getSampleRate() / (double) minSampleRate; - if (factor != Math.rint(factor)) { // Not an integer - throw new RuntimeException("Sample rates must be a multiple of the lowest sample rate to create a correct file!"); - } - for (int i = 0; i < syncSamples.length; i++) { - syncSamples[i] = (long) (1 + syncSamples[i] * factor / (double) samplesPerFrame); - } - getSampleNumbersCache.put(key, syncSamples); - return syncSamples; - } - throw new RuntimeException("There was absolutely no Track with sync samples. I can't work with that!"); - } else { - // Ok, my track has no sync samples - let's find one with sync samples. - for (Track candidate : movie.getTracks()) { - if (candidate.getSyncSamples() != null && candidate.getSyncSamples().length > 0) { - long[] refSyncSamples = sampleNumbers(candidate, movie); - int refSampleCount = candidate.getSamples().size(); - - long[] syncSamples = new long[refSyncSamples.length]; - long sc = track.getSamples().size(); - double stretch = (double) sc / refSampleCount; - - for (int i = 0; i < syncSamples.length; i++) { - long start = (long) Math.ceil(stretch * (refSyncSamples[i] - 1)) + 1; - syncSamples[i] = start; - // The Stretch makes sure that there are as much audio and video chunks! - } - getSampleNumbersCache.put(key, syncSamples); - return syncSamples; - } - } - throw new RuntimeException("There was absolutely no Track with sync samples. I can't work with that!"); - } - - - } - - /** - * Calculates the timestamp of all tracks' sync samples. - * - * @param movie - * @param track - * @return - */ - public static List<long[]> getSyncSamplesTimestamps(Movie movie, Track track) { - List<long[]> times = new LinkedList<long[]>(); - for (Track currentTrack : movie.getTracks()) { - if (currentTrack.getHandler().equals(track.getHandler())) { - long[] currentTrackSyncSamples = currentTrack.getSyncSamples(); - if (currentTrackSyncSamples != null && currentTrackSyncSamples.length > 0) { - final long[] currentTrackTimes = getTimes(currentTrack, movie); - times.add(currentTrackTimes); - } - } - } - return times; - } - - public long[] getCommonIndices(long[] syncSamples, long[] syncSampleTimes, long timeScale, long[]... otherTracksTimes) { - List<Long> nuSyncSamples = new LinkedList<Long>(); - List<Long> nuSyncSampleTimes = new LinkedList<Long>(); - - - for (int i = 0; i < syncSampleTimes.length; i++) { - boolean foundInEveryRef = true; - for (long[] times : otherTracksTimes) { - foundInEveryRef &= (Arrays.binarySearch(times, syncSampleTimes[i]) >= 0); - } - - if (foundInEveryRef) { - // use sample only if found in every other track. - nuSyncSamples.add(syncSamples[i]); - nuSyncSampleTimes.add(syncSampleTimes[i]); - } - } - // We have two arrays now: - // nuSyncSamples: Contains all common sync samples - // nuSyncSampleTimes: Contains the times of all sync samples - - // Start: Warn user if samples are not matching! - if (nuSyncSamples.size() < (syncSamples.length * 0.25)) { - String log = ""; - log += String.format("%5d - Common: [", nuSyncSamples.size()); - for (long l : nuSyncSamples) { - log += (String.format("%10d,", l)); - } - log += ("]"); - LOG.warning(log); - log = ""; - - log += String.format("%5d - In : [", syncSamples.length); - for (long l : syncSamples) { - log += (String.format("%10d,", l)); - } - log += ("]"); - LOG.warning(log); - LOG.warning("There are less than 25% of common sync samples in the given track."); - throw new RuntimeException("There are less than 25% of common sync samples in the given track."); - } else if (nuSyncSamples.size() < (syncSamples.length * 0.5)) { - LOG.fine("There are less than 50% of common sync samples in the given track. This is implausible but I'm ok to continue."); - } else if (nuSyncSamples.size() < syncSamples.length) { - LOG.finest("Common SyncSample positions vs. this tracks SyncSample positions: " + nuSyncSamples.size() + " vs. " + syncSamples.length); - } - // End: Warn user if samples are not matching! - - - - - List<Long> finalSampleList = new LinkedList<Long>(); - - if (minFragmentDurationSeconds > 0) { - // if minFragmentDurationSeconds is greater 0 - // we need to throw away certain samples. - long lastSyncSampleTime = -1; - Iterator<Long> nuSyncSamplesIterator = nuSyncSamples.iterator(); - Iterator<Long> nuSyncSampleTimesIterator = nuSyncSampleTimes.iterator(); - while (nuSyncSamplesIterator.hasNext() && nuSyncSampleTimesIterator.hasNext()) { - long curSyncSample = nuSyncSamplesIterator.next(); - long curSyncSampleTime = nuSyncSampleTimesIterator.next(); - if (lastSyncSampleTime == -1 || (curSyncSampleTime - lastSyncSampleTime) / timeScale >= minFragmentDurationSeconds) { - finalSampleList.add(curSyncSample); - lastSyncSampleTime = curSyncSampleTime; - } - } - } else { - // the list of all samples is the final list of samples - // since minFragmentDurationSeconds ist not used. - finalSampleList = nuSyncSamples; - } - - - // transform the list to an array - long[] finalSampleArray = new long[finalSampleList.size()]; - for (int i = 0; i < finalSampleArray.length; i++) { - finalSampleArray[i] = finalSampleList.get(i); - } - return finalSampleArray; - - } - - - private static long[] getTimes(Track track, Movie m) { - final CacheTuple key = new CacheTuple(track, m); - final long[] result = getTimesCache.get(key); - if (result != null) { - return result; - } - - long[] syncSamples = track.getSyncSamples(); - long[] syncSampleTimes = new long[syncSamples.length]; - Queue<TimeToSampleBox.Entry> timeQueue = new LinkedList<TimeToSampleBox.Entry>(track.getDecodingTimeEntries()); - - int currentSample = 1; // first syncsample is 1 - long currentDuration = 0; - long currentDelta = 0; - int currentSyncSampleIndex = 0; - long left = 0; - - final long scalingFactor = calculateTracktimesScalingFactor(m, track); - - while (currentSample <= syncSamples[syncSamples.length - 1]) { - if (currentSample++ == syncSamples[currentSyncSampleIndex]) { - syncSampleTimes[currentSyncSampleIndex++] = currentDuration * scalingFactor; - } - if (left-- == 0) { - TimeToSampleBox.Entry entry = timeQueue.poll(); - left = entry.getCount() - 1; - currentDelta = entry.getDelta(); - } - currentDuration += currentDelta; - } - getTimesCache.put(key, syncSampleTimes); - return syncSampleTimes; - } - - private static long calculateTracktimesScalingFactor(Movie m, Track track) { - long timeScale = 1; - for (Track track1 : m.getTracks()) { - if (track1.getHandler().equals(track.getHandler())) { - if (track1.getTrackMetaData().getTimescale() != track.getTrackMetaData().getTimescale()) { - timeScale = lcm(timeScale, track1.getTrackMetaData().getTimescale()); - } - } - } - return timeScale; - } - - public static class CacheTuple { - Track track; - Movie movie; - - public CacheTuple(Track track, Movie movie) { - this.track = track; - this.movie = movie; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - CacheTuple that = (CacheTuple) o; - - if (movie != null ? !movie.equals(that.movie) : that.movie != null) return false; - if (track != null ? !track.equals(that.track) : that.track != null) return false; - - return true; - } - - @Override - public int hashCode() { - int result = track != null ? track.hashCode() : 0; - result = 31 * result + (movie != null ? movie.hashCode() : 0); - return result; - } - } -} diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/.svn/text-base/TwoSecondIntersectionFinder.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/.svn/text-base/TwoSecondIntersectionFinder.java.svn-base deleted file mode 100644 index 88aa4ab..0000000 --- a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/builder/.svn/text-base/TwoSecondIntersectionFinder.java.svn-base +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright 2012 Sebastian Annies, Hamburg - * - * Licensed under the Apache License, Version 2.0 (the License); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an AS IS BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.googlecode.mp4parser.authoring.builder; - -import com.coremedia.iso.boxes.TimeToSampleBox; -import com.googlecode.mp4parser.authoring.Movie; -import com.googlecode.mp4parser.authoring.Track; - -import java.util.Arrays; -import java.util.List; - -/** - * This <code>FragmentIntersectionFinder</code> cuts the input movie in 2 second - * snippets. - */ -public class TwoSecondIntersectionFinder implements FragmentIntersectionFinder { - - protected long getDuration(Track track) { - long duration = 0; - for (TimeToSampleBox.Entry entry : track.getDecodingTimeEntries()) { - duration += entry.getCount() * entry.getDelta(); - } - return duration; - } - - /** - * {@inheritDoc} - */ - public long[] sampleNumbers(Track track, Movie movie) { - List<TimeToSampleBox.Entry> entries = track.getDecodingTimeEntries(); - - double trackLength = 0; - for (Track thisTrack : movie.getTracks()) { - double thisTracksLength = getDuration(thisTrack) / thisTrack.getTrackMetaData().getTimescale(); - if (trackLength < thisTracksLength) { - trackLength = thisTracksLength; - } - } - - int fragmentCount = (int)Math.ceil(trackLength / 2) - 1; - if (fragmentCount < 1) { - fragmentCount = 1; - } - - long fragments[] = new long[fragmentCount]; - Arrays.fill(fragments, -1); - fragments[0] = 1; - - long time = 0; - int samples = 0; - for (TimeToSampleBox.Entry entry : entries) { - for (int i = 0; i < entry.getCount(); i++) { - int currentFragment = (int) (time / track.getTrackMetaData().getTimescale() / 2) + 1; - if (currentFragment >= fragments.length) { - break; - } - fragments[currentFragment] = samples++ + 1; - time += entry.getDelta(); - } - } - long last = samples + 1; - // fill all -1 ones. - for (int i = fragments.length - 1; i >= 0; i--) { - if (fragments[i] == -1) { - fragments[i] = last ; - } - last = fragments[i]; - } - return fragments; - - } - -} |