Skip to content
Commits on Source (4)
......@@ -5,17 +5,17 @@
<artifactId>examples</artifactId>
<name>ISO Parser Examples</name>
<version>1.1.18</version>
<version>1.1.22</version>
<parent>
<groupId>com.googlecode.mp4parser</groupId>
<artifactId>mp4parser-project</artifactId>
<version>1.1.18</version>
<version>1.1.22</version>
</parent>
<dependencies>
<dependency>
<groupId>com.googlecode.mp4parser</groupId>
<artifactId>isoparser</artifactId>
<version>1.1.18</version>
<version>1.1.22</version>
</dependency>
<dependency>
<groupId>xom</groupId>
......@@ -79,7 +79,7 @@
<scm>
<url>http://code.google.com/p/mp4parser/source/browse/</url>
<connection>scm:svn:https://mp4parser.googlecode.com/svn/trunk/examples</connection>
<tag> mp4parser-project-1.1.18</tag>
<tag> mp4parser-project-1.1.22</tag>
</scm>
<repositories>
<repository>
......
......@@ -7,7 +7,7 @@
<description>A generic parser and writer for all ISO 14496 based files (MP4, Quicktime, DCF, PDCF, ...)
</description>
<url>http://code.google.com/p/mp4parser/</url>
<version>1.1.18</version>
<version>1.1.22</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
......@@ -197,7 +197,7 @@
<scm>
<url>https://github.com/sannies/mp4parser</url>
<tag> mp4parser-project-1.1.18</tag>
<tag> mp4parser-project-1.1.22</tag>
</scm>
<licenses>
......
......@@ -110,7 +110,7 @@ public class MediaHeaderBox extends AbstractFullBox {
creationTime = DateHelper.convert(IsoTypeReader.readUInt32(content));
modificationTime = DateHelper.convert(IsoTypeReader.readUInt32(content));
timescale = IsoTypeReader.readUInt32(content);
duration = content.getInt();
duration = IsoTypeReader.readUInt32(content);
}
if (duration < -1) {
LOG.logWarn("mdhd duration is not in expected range");
......
......@@ -20,12 +20,15 @@ import com.coremedia.iso.BoxParser;
import com.coremedia.iso.IsoTypeReader;
import com.coremedia.iso.IsoTypeWriter;
import com.googlecode.mp4parser.AbstractContainerBox;
import com.googlecode.mp4parser.DataSource;
import com.googlecode.mp4parser.MemoryDataSourceImpl;
import java.io.IOException;
import java.nio.ByteBuffer;
import com.googlecode.mp4parser.DataSource;
import java.nio.channels.WritableByteChannel;
import static com.googlecode.mp4parser.util.CastUtils.l2i;
/**
* <h1>4cc = "{@value #TYPE}"</h1>
......@@ -34,9 +37,15 @@ import java.nio.channels.WritableByteChannel;
public class MetaBox extends AbstractContainerBox {
public static final String TYPE = "meta";
private boolean isFullBox = true; // default is fullbox cause that's what ISO defines, simple box is apple specifc
private int version;
private int flags;
public MetaBox() {
super(TYPE);
}
public int getVersion() {
return version;
}
......@@ -70,30 +79,41 @@ public class MetaBox extends AbstractContainerBox {
IsoTypeWriter.writeUInt24(bb, flags);
}
public MetaBox() {
super(TYPE);
}
@Override
public void parse(DataSource dataSource, ByteBuffer header, long contentSize, BoxParser boxParser) throws IOException {
ByteBuffer bb = ByteBuffer.allocate(4);
ByteBuffer bb = ByteBuffer.allocate(l2i(contentSize));
dataSource.read(bb);
parseVersionAndFlags((ByteBuffer) bb.rewind());
initContainer(dataSource, contentSize - 4, boxParser);
bb.position(4);
String isHdlr = IsoTypeReader.read4cc(bb);
if ("hdlr".equals(isHdlr)) {
isFullBox = false;
initContainer(new MemoryDataSourceImpl((ByteBuffer) bb.rewind()), contentSize, boxParser);
// we got apple specifc box here
} else {
isFullBox = true;
parseVersionAndFlags((ByteBuffer) bb.rewind());
initContainer(new MemoryDataSourceImpl(bb), contentSize - 4, boxParser);
}
}
@Override
public void getBox(WritableByteChannel writableByteChannel) throws IOException {
writableByteChannel.write(getHeader());
ByteBuffer bb = ByteBuffer.allocate(4);
writeVersionAndFlags(bb);
writableByteChannel.write((ByteBuffer) bb.rewind());
if (isFullBox) {
ByteBuffer bb = ByteBuffer.allocate(4);
writeVersionAndFlags(bb);
writableByteChannel.write((ByteBuffer) bb.rewind());
}
writeContainer(writableByteChannel);
}
@Override
public long getSize() {
long s = getContainerSize();
long t = 4; // bytes to container start
long t = 0; // bytes to container start
if (isFullBox) {
t += 4;
}
return s + t + ((largeBox || (s + t) >= (1L << 32)) ? 16 : 8);
}
......
......@@ -56,4 +56,9 @@ public class UnknownBox extends AbstractBox {
public void setData(ByteBuffer data) {
this.data = data;
}
@Override
public String toString() {
return getClass().getName() + "[" + getType() + "]@" + Integer.toHexString(hashCode());
}
}
......@@ -147,38 +147,14 @@ public class TrackFragmentHeaderBox extends AbstractFullBox {
return trackId;
}
public long getBaseDataOffset() {
return baseDataOffset;
}
public long getSampleDescriptionIndex() {
return sampleDescriptionIndex;
}
public long getDefaultSampleDuration() {
return defaultSampleDuration;
}
public long getDefaultSampleSize() {
return defaultSampleSize;
}
public SampleFlags getDefaultSampleFlags() {
return defaultSampleFlags;
}
public boolean isDurationIsEmpty() {
return durationIsEmpty;
}
public boolean isDefaultBaseIsMoof() {
return defaultBaseIsMoof;
}
public void setTrackId(long trackId) {
this.trackId = trackId;
}
public long getBaseDataOffset() {
return baseDataOffset;
}
public void setBaseDataOffset(long baseDataOffset) {
if (baseDataOffset == -1) {
setFlags(getFlags() & (Integer.MAX_VALUE ^ 0x1));
......@@ -188,6 +164,10 @@ public class TrackFragmentHeaderBox extends AbstractFullBox {
this.baseDataOffset = baseDataOffset;
}
public long getSampleDescriptionIndex() {
return sampleDescriptionIndex;
}
public void setSampleDescriptionIndex(long sampleDescriptionIndex) {
if (sampleDescriptionIndex == -1) {
setFlags(getFlags() & (Integer.MAX_VALUE ^ 0x2));
......@@ -197,28 +177,66 @@ public class TrackFragmentHeaderBox extends AbstractFullBox {
this.sampleDescriptionIndex = sampleDescriptionIndex;
}
public long getDefaultSampleDuration() {
return defaultSampleDuration;
}
public void setDefaultSampleDuration(long defaultSampleDuration) {
setFlags(getFlags() | 0x8); // activate the field
this.defaultSampleDuration = defaultSampleDuration;
}
public long getDefaultSampleSize() {
return defaultSampleSize;
}
public void setDefaultSampleSize(long defaultSampleSize) {
setFlags(getFlags() | 0x10); // activate the field
if (defaultSampleSize != -1) {
setFlags(getFlags() | 0x10);
} else {
setFlags(getFlags() & (0xFFFFFF ^ 0x10));
}
this.defaultSampleSize = defaultSampleSize;
}
public SampleFlags getDefaultSampleFlags() {
return defaultSampleFlags;
}
public void setDefaultSampleFlags(SampleFlags defaultSampleFlags) {
setFlags(getFlags() | 0x20); // activate the field
if (defaultSampleFlags != null) {
setFlags(getFlags() | 0x20);
} else {
setFlags(getFlags() & (0xFFFFFF ^ 0x20));
}
this.defaultSampleFlags = defaultSampleFlags;
}
public boolean isDurationIsEmpty() {
return durationIsEmpty;
}
public void setDurationIsEmpty(boolean durationIsEmpty) {
setFlags(getFlags() | 0x10000); // activate the field
if (defaultBaseIsMoof) {
setFlags(getFlags() | 0x10000);
} else {
setFlags(getFlags() & (0xFFFFFF ^ 0x10000));
}
this.durationIsEmpty = durationIsEmpty;
}
public boolean isDefaultBaseIsMoof() {
return defaultBaseIsMoof;
}
public void setDefaultBaseIsMoof(boolean defaultBaseIsMoof) {
setFlags(getFlags() | 0x20000); // activate the field
if (defaultBaseIsMoof) {
setFlags(getFlags() | 0x20000);
} else {
setFlags(getFlags() & (0xFFFFFF ^ 0x20000));
}
this.defaultBaseIsMoof = defaultBaseIsMoof;
}
......
......@@ -58,7 +58,7 @@ public class FileDataSourceImpl implements DataSource {
}
public synchronized ByteBuffer map(long startPosition, long size) throws IOException {
LOG.logDebug(startPosition + " " + size);
//LOG.logDebug(startPosition + " " + size);
return fc.map(FileChannel.MapMode.READ_ONLY, startPosition, size);
}
......
package com.googlecode.mp4parser.authoring.builder;
import com.googlecode.mp4parser.authoring.Track;
import com.googlecode.mp4parser.util.Mp4Arrays;
import static java.util.Arrays.binarySearch;
/**
* Created by sannies on 26.03.2016.
*/
public class BetterFragmenter implements Fragmenter {
private double targetDuration;
public BetterFragmenter(double targetDuration) {
this.targetDuration = targetDuration;
}
public long[] sampleNumbers(Track track) {
long ts = track.getTrackMetaData().getTimescale();
long targetTicks = (long) (targetDuration * ts);
long[] fragments = new long[0];
long[] syncSamples = track.getSyncSamples();
long[] durations = track.getSampleDurations();
if (syncSamples != null) {
long[] syncSampleTicks = new long[syncSamples.length];
long ticks = 0;
long duration = track.getDuration();
for (int i = 0; i < durations.length; i++) {
int pos = binarySearch(syncSamples, (long) i + 1);
if (pos >= 0) {
syncSampleTicks[pos] = ticks;
}
ticks += durations[i];
}
long nextTargetTick = 0;
for (int currentSyncSampleIndex = 0; currentSyncSampleIndex < syncSampleTicks.length - 1; currentSyncSampleIndex++) {
long tickN1 = syncSampleTicks[currentSyncSampleIndex];
long tickN2 = syncSampleTicks[currentSyncSampleIndex + 1];
if (nextTargetTick <= tickN2) {
if (Math.abs(tickN1 - nextTargetTick) < Math.abs(tickN2 - nextTargetTick)) {
fragments = Mp4Arrays.copyOfAndAppend(fragments, syncSamples[currentSyncSampleIndex]);
nextTargetTick = syncSampleTicks[currentSyncSampleIndex] + targetTicks;
}
}
}
if (duration - syncSampleTicks[syncSampleTicks.length - 1] > targetTicks / 2) {
fragments = Mp4Arrays.copyOfAndAppend(fragments, syncSamples[syncSampleTicks.length - 1]);
}
} else {
double time = 0.0D;
fragments = new long[]{1L};
for (int i = 1; i < durations.length; ++i) {
time += (double) durations[i] / (double) ts;
if (time >= targetDuration) {
if (i > 0) {
fragments = Mp4Arrays.copyOfAndAppend(fragments, (long) (i + 1));
}
time = 0.0D;
}
}
if (time < targetDuration && fragments.length > 1) {
long numberSamplesLastTwoSegments = durations.length + 1 - fragments[fragments.length - 2];
fragments[fragments.length - 1] = fragments[fragments.length - 2] + numberSamplesLastTwoSegments / 2;
}
}
return fragments;
}
}
......@@ -52,7 +52,7 @@ public class DefaultFragmenterImpl implements Fragmenter {
}
}
// In case the last Fragment is shorter: make the previous one a bigger and omit the small one
if (time > 0 && time < fragmentLength && segmentStartSamples.length > 1) {
if (time < fragmentLength && segmentStartSamples.length > 1) {
long[] nuSegmentStartSamples = new long[segmentStartSamples.length - 1];
System.arraycopy(segmentStartSamples, 0, nuSegmentStartSamples, 0, segmentStartSamples.length - 1);
segmentStartSamples = nuSegmentStartSamples;
......
......@@ -89,7 +89,7 @@ public class DefaultMp4Builder implements Mp4Builder {
*/
public Container build(Movie movie) {
if (fragmenter == null) {
fragmenter = new DefaultFragmenterImpl(2);
fragmenter = new BetterFragmenter(2);
}
LOG.logDebug("Creating movie " + movie);
for (Track track : movie.getTracks()) {
......
......@@ -194,7 +194,7 @@ public class FragmentedMp4Builder implements Mp4Builder {
public Container build(Movie movie) {
LOG.fine("Creating movie " + movie);
if (fragmenter == null) {
fragmenter = new DefaultFragmenterImpl(2);
fragmenter = new BetterFragmenter(2);
}
BasicContainer isoFile = new BasicContainer();
......
package com.googlecode.mp4parser.authoring.builder;
import com.coremedia.iso.boxes.CompositionTimeToSample;
import com.coremedia.iso.boxes.SampleDependencyTypeBox;
import com.coremedia.iso.boxes.SampleDescriptionBox;
import com.coremedia.iso.boxes.SubSampleInformationBox;
import com.googlecode.mp4parser.authoring.Edit;
import com.googlecode.mp4parser.authoring.Sample;
import com.googlecode.mp4parser.authoring.Track;
import com.googlecode.mp4parser.authoring.TrackMetaData;
import com.googlecode.mp4parser.boxes.mp4.samplegrouping.GroupEntry;
import org.junit.Assert;
import org.junit.Test;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
public class BetterFragmenterTest {
public void verify(int total, long[] in, long[] expectedOut) {
Track t = new DummyTrack(total, in);
Fragmenter f = new BetterFragmenter(2.0);
long[] segmentStarter = f.sampleNumbers(t);
Assert.assertArrayEquals(expectedOut, segmentStarter);
}
@Test
public void testPatterns() throws Exception {
verify(151, null, new long[]{1, 51, 101, 126});
verify(121, null, new long[]{1, 51, 86});
verify(106, new long[]{1, 51, 62, 101}, new long[]{1, 51});
verify(151, new long[]{1, 6, 52, 101}, new long[]{1, 52, 101});
verify(151, new long[]{1, 51, 62, 101}, new long[]{1, 51, 101});
}
class DummyTrack implements Track {
long[] syncSamples;
long[] sampleDurations;
public DummyTrack(int total, long... syncSamples) {
this.syncSamples = syncSamples;
int lastSample = total;
sampleDurations = new long[lastSample];
Arrays.fill(sampleDurations, 40);
}
public SampleDescriptionBox getSampleDescriptionBox() {
return null;
}
public long[] getSampleDurations() {
return sampleDurations;
}
public long getDuration() {
long duration = 0;
for (long delta : getSampleDurations()) {
duration += delta;
}
return duration;
}
public List<CompositionTimeToSample.Entry> getCompositionTimeEntries() {
return null;
}
public long[] getSyncSamples() {
return syncSamples;
}
public List<SampleDependencyTypeBox.Entry> getSampleDependencies() {
return null;
}
public TrackMetaData getTrackMetaData() {
TrackMetaData tmd = new TrackMetaData();
tmd.setTimescale(1000);
return tmd;
}
public String getHandler() {
return null;
}
public List<Sample> getSamples() {
return null;
}
public SubSampleInformationBox getSubsampleInformationBox() {
return null;
}
public String getName() {
return null;
}
public List<Edit> getEdits() {
return null;
}
public Map<GroupEntry, long[]> getSampleGroups() {
return null;
}
public void close() throws IOException {
}
}
}
\ No newline at end of file
......@@ -18,14 +18,15 @@ public class DefaultFragmenterImplTest {
Movie movie = MovieCreator.build(this.getClass().getProtectionDomain().getCodeSource().getLocation().getFile() +
"/Beethoven - Bagatelle op.119 no.11 i.m4a");
long[] segments = new DefaultFragmenterImpl(2).sampleNumbers(movie.getTracks().get(0));
long[] segments = new BetterFragmenter(2).sampleNumbers(movie.getTracks().get(0));
Assert.assertArrayEquals(new
long[]{1, 87, 174, 261, 348, 435, 522, 609, 696, 783, 870, 957, 1044, 1131, 1218, 1305, 1392, 1479, 1566, 1653, 1740, 1827, 1914, 2001, 2088, 2175, 2262, 2349, 2436, 2523, 2610, 2697, 2784, 2871, 2958, 3045, 3132, 3219, 3306, 3393, 3480, 3567, 3654, 3741, 3828, 3915, 4002, 4089, 4176, 4263, 4350, 4437, 4524, 4611, 4698},
long[]{1, 88, 175, 262, 349, 436, 523, 610, 697, 784, 871, 958, 1045, 1132, 1219, 1306, 1393, 1480, 1567, 1654, 1741, 1828, 1915, 2002, 2089, 2176, 2263, 2350, 2437, 2524, 2611, 2698, 2785, 2872, 2959, 3046, 3133, 3220, 3307, 3394, 3481, 3568, 3655, 3742, 3829, 3916, 4003, 4090, 4177, 4264, 4351, 4438, 4525, 4612, 4699, 4775,},
segments
);
long[] segments2 = new DefaultFragmenterImpl(4).sampleNumbers(movie.getTracks().get(0));
long[] segments2 = new BetterFragmenter(4).sampleNumbers(movie.getTracks().get(0));
Assert.assertArrayEquals(new
long[]{1, 173, 346, 519, 692, 865, 1038, 1211, 1384, 1557, 1730, 1903, 2076, 2249, 2422, 2595, 2768, 2941, 3114, 3287, 3460, 3633, 3806, 3979, 4152, 4325, 4498, 4671,},
long[]{1, 174, 347, 520, 693, 866, 1039, 1212, 1385, 1558, 1731, 1904, 2077, 2250, 2423, 2596, 2769, 2942, 3115, 3288, 3461, 3634, 3807, 3980, 4153, 4326, 4499, 4672, 4762,},
segments2
);
......
......@@ -8,10 +8,7 @@ import com.googlecode.mp4parser.authoring.Track;
import com.googlecode.mp4parser.authoring.builder.DefaultMp4Builder;
import org.junit.Test;
import java.io.BufferedInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.channels.WritableByteChannel;
public class AC3TrackImplTest {
@Test
......@@ -22,7 +19,7 @@ public class AC3TrackImplTest {
DefaultMp4Builder mp4Builder = new DefaultMp4Builder();
Container c = mp4Builder.build(m);
//c.writeContainer(new FileOutputStream("C:\\dev\\mp4parser\\isoparser\\src\\test\\resources\\com\\googlecode\\mp4parser\\authoring\\tracks\\ac3-sample.mp4").getChannel());
//c.writeContainer(new FileOutputStream("C:\\dev\\mp4parser\\isoparser\\src\\test\\resources\\com\\googlecode\\mp4parser\\authoring\\tracks\\ac3-sample-2.mp4").getChannel());
IsoFile isoFileReference = new IsoFile(this.getClass().getProtectionDomain().getCodeSource().getLocation().getFile() + "/com/googlecode/mp4parser/authoring/tracks/ac3-sample.mp4");
BoxComparator.check(c, isoFileReference, "/moov[0]/mvhd[0]", "/moov[0]/trak[0]/tkhd[0]", "/moov[0]/trak[0]/mdia[0]/mdhd[0]");
}
......
......@@ -3,7 +3,7 @@
<groupId>com.googlecode.mp4parser</groupId>
<artifactId>mp4parser-project</artifactId>
<packaging>pom</packaging>
<version>1.1.18</version>
<version>1.1.22</version>
<name>MP4 Parser Project</name>
<url>http://code.google.com/p/mp4parser/</url>
<licenses>
......@@ -83,6 +83,6 @@
<scm>
<url>https://github.com/sannies/mp4parser</url>
<connection>scm:git:git@github.com:sannies/mp4parser.git</connection>
<tag> mp4parser-project-1.1.18</tag>
<tag> mp4parser-project-1.1.22</tag>
</scm>
</project>