Skip to content
Commits on Source (35)
......@@ -4,3 +4,11 @@
*.jar
*.war
*.ear
.idea
*.iml
examples/target
isoparser/target
isoparser/aac-sample.mp4
isoparser/ac3-sample.mp4
<component name="libraryTable">
<library name="Maven: commons-collections:commons-collections:3.2.1">
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/commons-collections/commons-collections/3.2.1/commons-collections-3.2.1.jar!/" />
</CLASSES>
<JAVADOC>
<root url="jar://$MAVEN_REPOSITORY$/commons-collections/commons-collections/3.2.1/commons-collections-3.2.1-javadoc.jar!/" />
</JAVADOC>
<SOURCES>
<root url="jar://$MAVEN_REPOSITORY$/commons-collections/commons-collections/3.2.1/commons-collections-3.2.1-sources.jar!/" />
</SOURCES>
</library>
</component>
\ No newline at end of file
language: java
\ No newline at end of file
* Build status: [![Build Status](https://travis-ci.org/sannies/mp4parser.svg?branch=master)](https://travis-ci.org/sannies/mp4parser)
* Current central released version: [![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.googlecode.mp4parser/isoparser/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.googlecode.mp4parser/isoparser)
Java MP4 Parser
====================
......@@ -8,6 +12,14 @@ Using the library
The library is published to Maven repositories. Each release is pushed to a staging repository which is published on the release page. On request specific releases can be pushed to maven central.
```
<dependency>
<groupId>com.googlecode.mp4parser</groupId>
<artifactId>isoparser</artifactId>
<version>1.1.7</version>
</dependency>
```
For projects that do not use a dependency management tool each release's artifacts (jar, javadoc-jar, source-jar) are attached to the release page. Please be aware that the project requires the aspectj-rt.jar library.
......@@ -19,10 +31,10 @@ Typical tasks for the MP4 Parser are:
- Muxing audio/video into an MP4 file
- Append recordings that use same encode settings
- Adding/Changing metadata
- Shorten recordings by ommiting frames.
- Shorten recordings by omitting frames
My examples will all use H264 and AAC as these to codecs are most typical for MP4 files. AC-3 is also not uncommon as the codec is well known from DVD.
There are also MP4 files with H263/MPEG-2 video tracks but they are no longer used widespread as most android phones You can also
My examples will all use H264 and AAC as these two codecs are most typical for MP4 files. AC-3 is also not uncommon as the codec is well known from DVD.
There are also MP4 files with H263/MPEG-2 video tracks but they are no longer used widespread as most android phones. You can also
Muxing Audio/Video
--------------------
......@@ -30,25 +42,29 @@ Muxing Audio/Video
The API and the process is straight-forward:
1. You wrap each raw format file into an appropriate Track object.
```java
H264TrackImpl h264Track = new H264TrackImpl(new FileDataSourceImpl("video.h264"));
AACTrackImpl aacTrack = new AACTrackImpl(new FileDataSourceImpl("audio.aac"));
```
2. These Track object are then added to a Movie object
```java
Movie movie = new Movie();
movie.addTrack(h264Track);
movie.addTrack(aacTrack);
```
3. The Movie object is fed into an MP4Builder to create the container.
```java
Container mp4file = new DefaultMp4Builder().build(movie);
```
4. Write the container to an appropriate sink.
```java
FileChannel fc = new FileOutputStream(new File("output.mp4")).getChannel();
mp4file.writeContainer(fc);
fc.close();
```
There are cases where the frame rate is signalled out of band or is known in advance so that the H264 doesn't contain it literally.
In this case you will have to supply it to the constructor.
......@@ -63,12 +79,12 @@ There are Track implementations for the following formats:
and additionally two subtitle tracks that do not directly wrap a raw format but they are conceptually similar.
Typical Issues
~~~~~~~~~~~~~~~
--------------------
Audio and video are not in sync. Whenever there are problems with timing possible make sure to start
Audio starts before video
~~~~~~~~~~~~~~~~~~~~~~~~~
--------------------
In AAC there are always samplerate/1024 sample/s so each sample's duration is 1000 * 1024 / samplerate milliseconds.
......@@ -80,11 +96,11 @@ to match audio and video exactly with that but the human perception is more sens
Remember: If someone is only 10 meters away the delay between audio and video is >30ms. The brain is used to that!
{code}
```java
AACTrackImpl aacTrackOriginal = new AACTrackImpl(new FileDataSourceImpl("audio.aac"));
// removes the first sample and shortens the AAC track by ~22ms
CroppedTrack aacTrackShort = new CroppedTrack(aacTrackOriginal, 1, aacTrack.getSamples().size());
{code}
```
......@@ -97,9 +113,6 @@ It is important to emphasize that you cannot append any two tracks with:
* Different resolutions
* Different frame-rates
as this leads to d
What can't you do?
--------------------
......
<?xml version="1.0" encoding="utf-8"?>
<tt xmlns="http://www.w3.org/ns/ttml" xmlns:ttm="http://www.w3.org/ns/ttml#metadata"
xmlns:tts="http://www.w3.org/ns/ttml#styling" xml:lang="en">
<head>
<metadata>
<ttm:title></ttm:title>
<ttm:desc></ttm:desc>
<ttm:copyright></ttm:copyright>
</metadata>
<styling>
<style xml:id="backgroundStyle" tts:backgroundColor="rgba(0,0,0,100)" tts:displayAlign="center"
tts:extent="80% 10%" tts:fontFamily="proportionalSansSerif" tts:fontSize="16px" tts:origin="10% 85%"
tts:textAlign="center"/>
<style style="backgroundStyle" xml:id="speakerStyle" tts:backgroundColor="transparent" tts:color="white"/>
</styling>
<layout>
<region style="speakerStyle" xml:id="speaker" tts:zIndex="1"/>
<region style="backgroundStyle" xml:id="background" tts:zIndex="0"/>
</layout>
</head>
<body>
<div style="default" xml:lang="en">
<p begin="00:00:01.000" end="00:00:03.000" region="speaker">This is pretty freaky.</p>
<p begin="00:00:57.000" end="00:00:59.000" region="speaker">Shouldn&apos;t you be down there?</p>
</div>
</body>
</tt>
\ No newline at end of file
......@@ -18,7 +18,7 @@
<dependency>
<groupId>com.googlecode.mp4parser</groupId>
<artifactId>isoparser</artifactId>
<version>1.0-RC-23-SNAPSHOT</version>
<version>1.0.4.2</version>
</dependency>
</dependencies>
<build>
......@@ -29,7 +29,7 @@
<plugin>
<groupId>com.jayway.maven.plugins.android.generation2</groupId>
<artifactId>android-maven-plugin</artifactId>
<version>3.4.0</version>
<version>4.0.0-rc.2</version>
<extensions>true</extensions>
</plugin>
</plugins>
......
......@@ -5,17 +5,17 @@
<artifactId>examples</artifactId>
<name>ISO Parser Examples</name>
<version>1.0.4.2</version>
<version>1.1.18</version>
<parent>
<groupId>com.googlecode.mp4parser</groupId>
<artifactId>mp4parser-project</artifactId>
<version>1.0.4.2</version>
<version>1.1.18</version>
</parent>
<dependencies>
<dependency>
<groupId>com.googlecode.mp4parser</groupId>
<artifactId>isoparser</artifactId>
<version>1.0.4.2</version>
<version>1.1.18</version>
</dependency>
<dependency>
<groupId>xom</groupId>
......@@ -42,6 +42,11 @@
<artifactId>jetty-server</artifactId>
<version>8.1.7.v20120910</version>
</dependency>
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.2.1</version>
</dependency>
</dependencies>
......@@ -74,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.0.4.2-2</tag>
<tag> mp4parser-project-1.1.18</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.0.4.2</version>
<version>1.1.18</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
......@@ -22,41 +22,6 @@
</resources>
<defaultGoal>install</defaultGoal>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-site-plugin</artifactId>
<version>3.0</version>
<configuration>
<reportPlugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jxr-plugin</artifactId>
<version>2.3</version>
<reportSets>
<reportSet>
<id>html</id>
<reports>
<report>jxr</report>
</reports>
</reportSet>
</reportSets>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>2.8.1</version>
<reportSets>
<reportSet>
<id>html</id>
<reports>
<report>javadoc</report>
</reports>
</reportSet>
</reportSets>
</plugin>
</reportPlugins>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
......@@ -84,6 +49,11 @@
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>2.4</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
......@@ -99,29 +69,26 @@
</configuration>
</plugin>
<plugin>
<inherited>true</inherited>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>2.1.2</version>
<artifactId>maven-shade-plugin</artifactId>
<version>2.4.1</version>
<executions>
<execution>
<id>attach-sources</id>
<phase>verify</phase>
<goals>
<goal>jar</goal>
<goal>shade</goal>
</goals>
</execution>
</executions>
<configuration>
<createSourcesJar>true</createSourcesJar>
<relocations>
<relocation>
<pattern>org.aspectj</pattern>
<shadedPattern>org.mp4parser.aspectj</shadedPattern>
</relocation>
</relocations>
</configuration>
</plugin>
</plugins>
<extensions>
<extension>
<groupId>com.google.code.maven-svn-wagon</groupId>
<artifactId>maven-svn-wagon</artifactId>
<version>1.4</version>
</extension>
</extensions>
</build>
<dependencies>
......@@ -167,19 +134,6 @@
</activation>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>2.1.2</version>
<executions>
<execution>
<id>attach-sources</id>
<goals>
<goal>jar-no-fork</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
......@@ -193,6 +147,7 @@
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
......@@ -211,8 +166,8 @@
</mailingLists>
<issueManagement>
<system>google-code</system>
<url>http://code.google.com/p/mp4parser/issues/list</url>
<system>github</system>
<url>https://github.com/sannies/mp4parser/issues</url>
</issueManagement>
<developers>
......@@ -226,7 +181,7 @@
<snapshotRepository>
<id>release</id>
<name>Sonatype Nexus Snapshots</name>
<url>https://repository.castlabs.com/content/repositories/snapshots/</url>
<url>http://repository.castlabs.com/content/repositories/snapshots/</url>
</snapshotRepository>
<repository>
<id>sonatype-nexus-staging</id>
......@@ -242,12 +197,12 @@
<scm>
<url>https://github.com/sannies/mp4parser</url>
<tag>mp4parser-project-1.0.4.2-2</tag>
<tag> mp4parser-project-1.1.18</tag>
</scm>
<licenses>
<license>
<name>Apache 2</name>
<name>Apache Software License - Version 2.0</name>
<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
<distribution>repo</distribution>
<comments>A business-friendly OSS license</comments>
......
......@@ -101,7 +101,7 @@ public abstract class AbstractBoxParser implements BoxParser {
Box box = createBox(type, usertype, (parent instanceof Box) ? ((Box) parent).getType() : "");
box.setParent(parent);
//LOG.finest("Parsing " + box.getType());
// System.out.println("parsing " + Arrays.toString(box.getType()) + " " + box.getClass().getName() + " size=" + size);
// System.out.println("parsing " + Mp4Arrays.toString(box.getType()) + " " + box.getClass().getName() + " size=" + size);
header.get().rewind();
box.parse(byteChannel, header.get(), contentSize, this);
......
......@@ -156,30 +156,17 @@ public final class IsoTypeReader {
}
private static IntHashMap codeCache = new IntHashMap();
private static byte[] codeBytes = new byte[4];
public static String read4cc(ByteBuffer bb) {
byte[] codeBytes = new byte[4];
bb.get(codeBytes);
int result = ((codeBytes[0] << 24) & 0xFF000000);
result |= ((codeBytes[1] << 16) & 0xFF0000);
result |= ((codeBytes[2] << 8) & 0xFF00);
result |= ((codeBytes[3]) & 0xFF);
String code;
if ((code = (String) codeCache.get(result)) != null) {
return code;
} else {
try {
code = new String(codeBytes, "ISO-8859-1");
codeCache.put(result, code);
return code;
return new String(codeBytes, "ISO-8859-1");
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
}
public static long readUInt48(ByteBuffer byteBuffer) {
......
......@@ -81,9 +81,9 @@ public class PropertyBoxParserImpl extends AbstractBoxParser {
public Box createBox(String type, byte[] userType, String parent) {
invoke(type, userType, parent);
String[] param = this.param.get();
try {
Class<Box> clazz = (Class<Box>) Class.forName(clazzName);
Class<Box> clazz = (Class<Box>) Class.forName(clazzName.get());
if (param.length > 0) {
Class[] constructorArgsClazz = new Class[param.length];
Object[] constructorArgs = new Object[param.length];
......@@ -123,8 +123,8 @@ public class PropertyBoxParserImpl extends AbstractBoxParser {
StringBuilder buildLookupStrings = new StringBuilder();
String clazzName;
String param[];
ThreadLocal<String> clazzName = new ThreadLocal<String>();
ThreadLocal<String[]> param = new ThreadLocal<String[]>();
static String[] EMPTY_STRING_ARRAY = new String[0];
public void invoke(String type, byte[] userType, String parent) {
......@@ -156,19 +156,19 @@ public class PropertyBoxParserImpl extends AbstractBoxParser {
throw new RuntimeException("No box object found for " + type);
}
if (!constructor.endsWith(")")) {
param = EMPTY_STRING_ARRAY;
clazzName = constructor;
param.set( EMPTY_STRING_ARRAY);
clazzName.set(constructor);
} else {
Matcher m = constuctorPattern.matcher(constructor);
boolean matches = m.matches();
if (!matches) {
throw new RuntimeException("Cannot work with that constructor: " + constructor);
}
clazzName = m.group(1);
clazzName.set( m.group(1));
if (m.group(2).length() == 0) {
param = EMPTY_STRING_ARRAY;
param.set(EMPTY_STRING_ARRAY);
} else {
param = m.group(2).length() > 0 ? m.group(2).split(",") : new String[]{};
param.set(m.group(2).length() > 0 ? m.group(2).split(",") : new String[]{});
}
}
......
......@@ -24,6 +24,11 @@ public class ChunkOffset64BitBox extends ChunkOffsetBox {
return chunkOffsets;
}
@Override
public void setChunkOffsets(long[] chunkOffsets) {
this.chunkOffsets = chunkOffsets;
}
@Override
protected long getContentSize() {
return 8 + 8 * chunkOffsets.length;
......
......@@ -13,6 +13,7 @@ public abstract class ChunkOffsetBox extends AbstractFullBox {
public abstract long[] getChunkOffsets();
public abstract void setChunkOffsets(long[] chunkOffsets);
public String toString() {
return this.getClass().getSimpleName() + "[entryCount=" + getChunkOffsets().length + "]";
......
......@@ -100,7 +100,7 @@ public class FileTypeBox extends AbstractBox {
*
* @param minorVersion the version number of the major brand
*/
public void setMinorVersion(int minorVersion) {
public void setMinorVersion(long minorVersion) {
this.minorVersion = minorVersion;
}
......
......@@ -20,6 +20,7 @@ import com.coremedia.iso.IsoTypeReader;
import com.coremedia.iso.IsoTypeWriter;
import com.googlecode.mp4parser.AbstractFullBox;
import com.googlecode.mp4parser.util.DateHelper;
import com.googlecode.mp4parser.util.Logger;
import java.nio.ByteBuffer;
import java.util.Date;
......@@ -30,6 +31,7 @@ import java.util.Date;
* considered as a whole.
*/
public class MediaHeaderBox extends AbstractFullBox {
private static Logger LOG = Logger.getLogger(MediaHeaderBox.class);
public static final String TYPE = "mdhd";
......@@ -103,13 +105,18 @@ public class MediaHeaderBox extends AbstractFullBox {
creationTime = DateHelper.convert(IsoTypeReader.readUInt64(content));
modificationTime = DateHelper.convert(IsoTypeReader.readUInt64(content));
timescale = IsoTypeReader.readUInt32(content);
duration = IsoTypeReader.readUInt64(content);
duration = content.getLong();
} else {
creationTime = DateHelper.convert(IsoTypeReader.readUInt32(content));
modificationTime = DateHelper.convert(IsoTypeReader.readUInt32(content));
timescale = IsoTypeReader.readUInt32(content);
duration = IsoTypeReader.readUInt32(content);
duration = content.getInt();
}
if (duration < -1) {
LOG.logWarn("mdhd duration is not in expected range");
}
language = IsoTypeReader.readIso639(content);
IsoTypeReader.readUInt16(content);
}
......@@ -137,12 +144,12 @@ public class MediaHeaderBox extends AbstractFullBox {
IsoTypeWriter.writeUInt64(byteBuffer, DateHelper.convert(creationTime));
IsoTypeWriter.writeUInt64(byteBuffer, DateHelper.convert(modificationTime));
IsoTypeWriter.writeUInt32(byteBuffer, timescale);
IsoTypeWriter.writeUInt64(byteBuffer, duration);
byteBuffer.putLong(duration);
} else {
IsoTypeWriter.writeUInt32(byteBuffer, DateHelper.convert(creationTime));
IsoTypeWriter.writeUInt32(byteBuffer, DateHelper.convert(modificationTime));
IsoTypeWriter.writeUInt32(byteBuffer, timescale);
IsoTypeWriter.writeUInt32(byteBuffer, duration);
byteBuffer.putInt((int) duration);
}
IsoTypeWriter.writeIso639(byteBuffer, language);
IsoTypeWriter.writeUInt16(byteBuffer, 0);
......
......@@ -20,6 +20,7 @@ import com.coremedia.iso.IsoTypeReader;
import com.coremedia.iso.IsoTypeWriter;
import com.googlecode.mp4parser.AbstractFullBox;
import com.googlecode.mp4parser.util.DateHelper;
import com.googlecode.mp4parser.util.Logger;
import com.googlecode.mp4parser.util.Matrix;
import java.nio.ByteBuffer;
......@@ -37,6 +38,8 @@ import java.util.Date;
* considered as a whole.
*/
public class MovieHeaderBox extends AbstractFullBox {
private static Logger LOG = Logger.getLogger(MovieHeaderBox.class);
private Date creationTime;
private Date modificationTime;
private long timescale;
......@@ -110,13 +113,19 @@ public class MovieHeaderBox extends AbstractFullBox {
creationTime = DateHelper.convert(IsoTypeReader.readUInt64(content));
modificationTime = DateHelper.convert(IsoTypeReader.readUInt64(content));
timescale = IsoTypeReader.readUInt32(content);
duration = IsoTypeReader.readUInt64(content);
duration = content.getLong();
} else {
creationTime = DateHelper.convert(IsoTypeReader.readUInt32(content));
modificationTime = DateHelper.convert(IsoTypeReader.readUInt32(content));
timescale = IsoTypeReader.readUInt32(content);
duration = IsoTypeReader.readUInt32(content);
duration = content.getInt();
}
if (duration < -1) {
LOG.logWarn("mvhd duration is not in expected range");
}
rate = IsoTypeReader.readFixedPoint1616(content);
volume = IsoTypeReader.readFixedPoint88(content);
IsoTypeReader.readUInt16(content);
......@@ -166,12 +175,12 @@ public class MovieHeaderBox extends AbstractFullBox {
IsoTypeWriter.writeUInt64(byteBuffer, DateHelper.convert(creationTime));
IsoTypeWriter.writeUInt64(byteBuffer, DateHelper.convert(modificationTime));
IsoTypeWriter.writeUInt32(byteBuffer, timescale);
IsoTypeWriter.writeUInt64(byteBuffer, duration);
byteBuffer.putLong(duration);
} else {
IsoTypeWriter.writeUInt32(byteBuffer, DateHelper.convert(creationTime));
IsoTypeWriter.writeUInt32(byteBuffer, DateHelper.convert(modificationTime));
IsoTypeWriter.writeUInt32(byteBuffer, timescale);
IsoTypeWriter.writeUInt32(byteBuffer, duration);
byteBuffer.putInt((int) duration);
}
IsoTypeWriter.writeFixedPoint1616(byteBuffer, rate);
IsoTypeWriter.writeFixedPoint88(byteBuffer, volume);
......
......@@ -29,7 +29,7 @@ import java.util.List;
* <pre>
* aligned(8) class SampleDependencyTypeBox extends FullBox('sdtp', version = 0, 0) {
* for (i=0; i &lt; sample_count; i++){
* unsigned int(2) reserved = 0;
* unsigned int(2) isLeading;
* unsigned int(2) sample_depends_on;
* unsigned int(2) sample_is_depended_on;
* unsigned int(2) sample_has_redundancy;
......@@ -51,11 +51,11 @@ public class SampleDependencyTypeBox extends AbstractFullBox {
private int value;
public int getReserved() {
public int getIsLeading() {
return (value >> 6) & 0x03;
}
public void setReserved(int res) {
public void setIsLeading(int res) {
value = (res & 0x03) << 6 | value & 0x3f;
}
......@@ -86,7 +86,7 @@ public class SampleDependencyTypeBox extends AbstractFullBox {
@Override
public String toString() {
return "Entry{" +
"reserved=" + getReserved() +
"isLeading=" + getIsLeading() +
", sampleDependsOn=" + getSampleDependsOn() +
", sampleIsDependentOn=" + getSampleIsDependentOn() +
", sampleHasRedundancy=" + getSampleHasRedundancy() +
......
......@@ -44,6 +44,7 @@ public class StaticChunkOffsetBox extends ChunkOffsetBox {
return 8 + chunkOffsets.length * 4;
}
@Override
public void setChunkOffsets(long[] chunkOffsets) {
this.chunkOffsets = chunkOffsets;
}
......
......@@ -21,6 +21,7 @@ import com.coremedia.iso.IsoTypeReader;
import com.coremedia.iso.IsoTypeWriter;
import com.googlecode.mp4parser.AbstractFullBox;
import com.googlecode.mp4parser.util.DateHelper;
import com.googlecode.mp4parser.util.Logger;
import com.googlecode.mp4parser.util.Matrix;
import java.nio.ByteBuffer;
......@@ -37,11 +38,12 @@ import java.util.Date;
* to 0, so that they are ignored for local playback and preview.
*/
public class TrackHeaderBox extends AbstractFullBox {
private static Logger LOG = Logger.getLogger(TrackHeaderBox.class);
public static final String TYPE = "tkhd";
private Date creationTime;
private Date modificationTime;
private Date creationTime = new Date(0);
private Date modificationTime = new Date(0);
private long trackId;
private long duration;
private int layer;
......@@ -117,17 +119,18 @@ public class TrackHeaderBox extends AbstractFullBox {
trackId = IsoTypeReader.readUInt32(content);
IsoTypeReader.readUInt32(content);
duration = content.getLong();
if (duration < -1) {
throw new RuntimeException("The tracks duration is bigger than Long.MAX_VALUE");
}
} else {
creationTime = DateHelper.convert(IsoTypeReader.readUInt32(content));
modificationTime = DateHelper.convert(IsoTypeReader.readUInt32(content));
trackId = IsoTypeReader.readUInt32(content);
IsoTypeReader.readUInt32(content);
duration = IsoTypeReader.readUInt32(content);
duration = content.getInt();
} // 196
if (duration < -1) {
LOG.logWarn("tkhd duration is not in expected range");
}
IsoTypeReader.readUInt32(content);
IsoTypeReader.readUInt32(content);
layer = IsoTypeReader.readUInt16(content); // 204
......@@ -146,13 +149,13 @@ public class TrackHeaderBox extends AbstractFullBox {
IsoTypeWriter.writeUInt64(byteBuffer, DateHelper.convert(modificationTime));
IsoTypeWriter.writeUInt32(byteBuffer, trackId);
IsoTypeWriter.writeUInt32(byteBuffer, 0);
IsoTypeWriter.writeUInt64(byteBuffer, duration);
byteBuffer.putLong(duration);
} else {
IsoTypeWriter.writeUInt32(byteBuffer, DateHelper.convert(creationTime));
IsoTypeWriter.writeUInt32(byteBuffer, DateHelper.convert(modificationTime));
IsoTypeWriter.writeUInt32(byteBuffer, trackId);
IsoTypeWriter.writeUInt32(byteBuffer, 0);
IsoTypeWriter.writeUInt32(byteBuffer, duration);
byteBuffer.putInt((int) duration);
} // 196
IsoTypeWriter.writeUInt32(byteBuffer, 0);
IsoTypeWriter.writeUInt32(byteBuffer, 0);
......
/*
* Copyright 2008 CoreMedia AG, 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.coremedia.iso.boxes;
import com.coremedia.iso.IsoTypeReader;
import com.coremedia.iso.IsoTypeWriter;
import com.googlecode.mp4parser.AbstractBox;
import java.nio.ByteBuffer;
/**
* <h1>4cc = "{@value #TYPE1} or {@value #TYPE2}"</h1>
* Contains a reference to a track. The type of the box gives the kind of reference.
*/
public class TrackReferenceTypeBox extends AbstractBox {
public static final String TYPE1 = "hint";
public static final String TYPE2 = "cdsc";
private long[] trackIds;
public TrackReferenceTypeBox(String type) {
super(type);
}
public long[] getTrackIds() {
return trackIds;
}
@Override
public void _parseDetails(ByteBuffer content) {
int count = (int) (content.remaining() / 4);
trackIds = new long[count];
for (int i = 0; i < count; i++) {
trackIds[i] = IsoTypeReader.readUInt32(content);
}
}
@Override
protected void getContent(ByteBuffer byteBuffer) {
for (long trackId : trackIds) {
IsoTypeWriter.writeUInt32(byteBuffer, trackId);
}
}
protected long getContentSize() {
return trackIds.length * 4;
}
public String toString() {
StringBuilder buffer = new StringBuilder();
buffer.append("TrackReferenceTypeBox[type=").append(getType());
for (int i = 0; i < trackIds.length; i++) {
buffer.append(";trackId");
buffer.append(i);
buffer.append("=");
buffer.append(trackIds[i]);
}
buffer.append("]");
return buffer.toString();
}
}