From c0155b597d6e17d05fd382666cd9c2c28ce73241 Mon Sep 17 00:00:00 2001
From: ddsouz
Date: Wed, 16 Mar 2016 18:58:49 -0700
Subject: [PATCH 01/42] Adding support for adding Simple Tags.
---
build.gradle | 14 ++++-
.../java/org/ebml/matroska/MatroskaFile.java | 19 +++----
.../ebml/matroska/MatroskaFileSimpleTag.java | 37 ++++++++++++--
.../ebml/matroska/MatroskaFileTagEntry.java | 35 ++++++++++++-
.../org/ebml/matroska/MatroskaFileTags.java | 51 +++++++++++++++++++
.../org/ebml/matroska/MatroskaFileTrack.java | 1 -
.../org/ebml/matroska/MatroskaFileWriter.java | 20 ++++++++
7 files changed, 158 insertions(+), 19 deletions(-)
create mode 100644 src/main/java/org/ebml/matroska/MatroskaFileTags.java
diff --git a/build.gradle b/build.gradle
index 6daf82e..7f397fb 100644
--- a/build.gradle
+++ b/build.gradle
@@ -3,8 +3,10 @@ apply plugin: 'maven'
apply plugin: 'findbugs'
apply plugin: 'checkstyle'
apply plugin: 'org.dm.bundle'
+apply plugin: 'maven-publish'
repositories {
+ mavenLocal()
mavenCentral()
}
@@ -56,7 +58,17 @@ sourceSets {
}
}
-project.version = '2.2'
+publishing {
+ publications {
+ "${project.name}Jar"(MavenPublication) {
+ from components.java
+ version project.version.toString()
+ artifactId project.name
+ }
+ }
+}
+
+project.version = '2.3.0'
bundle {
instructions << [
diff --git a/src/main/java/org/ebml/matroska/MatroskaFile.java b/src/main/java/org/ebml/matroska/MatroskaFile.java
index 00682e6..27af431 100644
--- a/src/main/java/org/ebml/matroska/MatroskaFile.java
+++ b/src/main/java/org/ebml/matroska/MatroskaFile.java
@@ -129,12 +129,10 @@ public void readFile()
if (level1.isType(MatroskaDocTypes.Info.getType()))
{
parseSegmentInfo(level1, level2);
-
}
else if (level1.isType(MatroskaDocTypes.Tracks.getType()))
{
parseTracks(level1, level2);
-
}
else if (level1.isType(MatroskaDocTypes.Cluster.getType()))
{
@@ -144,12 +142,10 @@ else if (level1.isType(MatroskaDocTypes.Cluster.getType()))
}
// Break out of this loop, we should only parse the first cluster
break;
-
}
- else if (level1.isType(MatroskaDocTypes.Tag.getType()))
+ else if (level1.isType(MatroskaDocTypes.Tags.getType()))
{
parseTags(level1, level2);
-
}
level1.skipData(ioDS);
@@ -547,13 +543,11 @@ private void parseTags(final Element level1, Element level2)
{
level4.readData(ioDS);
tag.trackUID.add(new Long(((UnsignedIntegerElement) level4).getValue()));
-
}
else if (level4.isType(MatroskaDocTypes.TagChapterUID.getType()))
{
level4.readData(ioDS);
tag.chapterUID.add(new Long(((UnsignedIntegerElement) level4).getValue()));
-
}
else if (level4.isType(MatroskaDocTypes.TagAttachmentUID.getType()))
{
@@ -564,11 +558,10 @@ else if (level4.isType(MatroskaDocTypes.TagAttachmentUID.getType()))
level4.skipData(ioDS);
level4 = ((MasterElement) level3).readNextChild(reader);
}
-
}
else if (level3.isType(MatroskaDocTypes.SimpleTag.getType()))
{
- tag.simpleTags.add(parseTagsSimpleTag(level3, level4));
+ tag.addSimpleTag(parseTagsSimpleTag(level3, level4));
}
level3.skipData(ioDS);
level3 = ((MasterElement) level2).readNextChild(reader);
@@ -591,14 +584,14 @@ private MatroskaFileSimpleTag parseTagsSimpleTag(final Element level3, Element l
if (level4.isType(MatroskaDocTypes.TagName.getType()))
{
level4.readData(ioDS);
- simpleTag.name = ((StringElement) level4).getValue();
-
+ String tagName = ((StringElement) level4).getValue();
+ simpleTag.setName(tagName);
}
else if (level4.isType(MatroskaDocTypes.TagString.getType()))
{
level4.readData(ioDS);
- simpleTag.value = ((StringElement) level4).getValue();
-
+ String tagString = ((StringElement) level4).getValue();
+ simpleTag.setValue(tagString);
}
level4.skipData(ioDS);
diff --git a/src/main/java/org/ebml/matroska/MatroskaFileSimpleTag.java b/src/main/java/org/ebml/matroska/MatroskaFileSimpleTag.java
index 95b0437..f9c7d91 100644
--- a/src/main/java/org/ebml/matroska/MatroskaFileSimpleTag.java
+++ b/src/main/java/org/ebml/matroska/MatroskaFileSimpleTag.java
@@ -20,12 +20,43 @@
package org.ebml.matroska;
import java.util.ArrayList;
+import java.util.List;
public class MatroskaFileSimpleTag
{
- public String name;
- public String value;
- public ArrayList children = new ArrayList<>();
+ private String name;
+ private String value;
+ private List children = new ArrayList<>();
+
+ public String getName()
+ {
+ return name;
+ }
+
+ public void setName(final String name)
+ {
+ this.name = name;
+ }
+
+ public String getValue()
+ {
+ return value;
+ }
+
+ public void setValue(final String value)
+ {
+ this.value = value;
+ }
+
+ public List getChildren()
+ {
+ return children;
+ }
+
+ public void setChildren(final List children)
+ {
+ this.children = children;
+ }
public String toString(int depth)
{
diff --git a/src/main/java/org/ebml/matroska/MatroskaFileTagEntry.java b/src/main/java/org/ebml/matroska/MatroskaFileTagEntry.java
index 7ebd67f..b6cf735 100644
--- a/src/main/java/org/ebml/matroska/MatroskaFileTagEntry.java
+++ b/src/main/java/org/ebml/matroska/MatroskaFileTagEntry.java
@@ -20,13 +20,46 @@
package org.ebml.matroska;
import java.util.ArrayList;
+import java.util.List;
+
+import org.ebml.Element;
+import org.ebml.MasterElement;
+import org.ebml.UTF8StringElement;
public class MatroskaFileTagEntry
{
public ArrayList trackUID = new ArrayList<>();
public ArrayList chapterUID = new ArrayList<>();
public ArrayList attachmentUID = new ArrayList<>();
- public ArrayList simpleTags = new ArrayList<>();
+ private List simpleTags = new ArrayList<>();
+
+ public void addSimpleTag(final MatroskaFileSimpleTag simpleTag)
+ {
+ simpleTags.add(simpleTag);
+ }
+
+ Element toElement()
+ {
+ final MasterElement tagEntryElem = MatroskaDocTypes.Tag.getInstance();
+
+ for (MatroskaFileSimpleTag simpleTag : simpleTags)
+ {
+ final MasterElement simpleTagEntryElem = MatroskaDocTypes.SimpleTag.getInstance();
+
+ final UTF8StringElement simpleTagNameElem = MatroskaDocTypes.TagName.getInstance();
+ simpleTagNameElem.setValue(simpleTag.getName());
+
+ final UTF8StringElement simpleTagStringElem = MatroskaDocTypes.TagString.getInstance();
+ simpleTagStringElem.setValue(simpleTag.getValue());
+
+ simpleTagEntryElem.addChildElement(simpleTagNameElem);
+ simpleTagEntryElem.addChildElement(simpleTagStringElem);
+
+ tagEntryElem.addChildElement(simpleTagEntryElem);
+ }
+
+ return tagEntryElem;
+ }
@Override
public String toString()
diff --git a/src/main/java/org/ebml/matroska/MatroskaFileTags.java b/src/main/java/org/ebml/matroska/MatroskaFileTags.java
new file mode 100644
index 0000000..35e5f96
--- /dev/null
+++ b/src/main/java/org/ebml/matroska/MatroskaFileTags.java
@@ -0,0 +1,51 @@
+package org.ebml.matroska;
+
+import java.util.ArrayList;
+
+import org.ebml.MasterElement;
+import org.ebml.io.DataWriter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class MatroskaFileTags
+{
+ private static final int BLOCK_SIZE = 4096;
+ private static final Logger LOG = LoggerFactory.getLogger(MatroskaFileTags.class);
+
+ private final ArrayList tags = new ArrayList<>();
+
+ private final long myPosition;
+
+ public MatroskaFileTags(final long position)
+ {
+ myPosition = position;
+ }
+
+ public void addTag(final MatroskaFileTagEntry tag)
+ {
+ tags.add(tag);
+ }
+
+ public long writeTags(final DataWriter ioDW)
+ {
+ final MasterElement tagsElem = MatroskaDocTypes.Tags.getInstance();
+
+ for (final MatroskaFileTagEntry tag: tags)
+ {
+ tagsElem.addChildElement(tag.toElement());
+ }
+ tagsElem.writeElement(ioDW);
+ assert BLOCK_SIZE > tagsElem.getTotalSize();
+ new VoidElement(BLOCK_SIZE - tagsElem.getTotalSize()).writeElement(ioDW);
+ return BLOCK_SIZE;
+ }
+
+ public void update(final DataWriter ioDW)
+ {
+ LOG.info("Updating tags list!");
+ final long start = ioDW.getFilePointer();
+ ioDW.seek(myPosition);
+ writeTags(ioDW);
+ ioDW.seek(start);
+ }
+}
diff --git a/src/main/java/org/ebml/matroska/MatroskaFileTrack.java b/src/main/java/org/ebml/matroska/MatroskaFileTrack.java
index 9a8f80f..d6ccf30 100644
--- a/src/main/java/org/ebml/matroska/MatroskaFileTrack.java
+++ b/src/main/java/org/ebml/matroska/MatroskaFileTrack.java
@@ -463,7 +463,6 @@ Element toElement()
final BinaryElement trackCodecPrivateElem = MatroskaDocTypes.CodecPrivate.getInstance();
trackCodecPrivateElem.setData(this.getCodecPrivate());
trackEntryElem.addChildElement(trackCodecPrivateElem);
-
}
final UnsignedIntegerElement trackDefaultDurationElem = MatroskaDocTypes.DefaultDuration.getInstance();
diff --git a/src/main/java/org/ebml/matroska/MatroskaFileWriter.java b/src/main/java/org/ebml/matroska/MatroskaFileWriter.java
index 560a4f7..6f5a408 100644
--- a/src/main/java/org/ebml/matroska/MatroskaFileWriter.java
+++ b/src/main/java/org/ebml/matroska/MatroskaFileWriter.java
@@ -40,6 +40,7 @@ public class MatroskaFileWriter
private final MatroskaCluster cluster;
private final MatroskaSegmentInfo segmentInfoElem;
private final MatroskaFileTracks tracks;
+ private final MatroskaFileTags tags;
/**
* @param outputDataWriter DataWriter to write out to.
@@ -59,6 +60,9 @@ public MatroskaFileWriter(final DataWriter outputDataWriter)
metaSeek.addIndexedElement(MatroskaDocTypes.Tracks.getType(), ioDW.getFilePointer());
tracks = new MatroskaFileTracks(ioDW.getFilePointer());
tracks.writeTracks(ioDW);
+ metaSeek.addIndexedElement(MatroskaDocTypes.Tags.getType(), ioDW.getFilePointer());
+ tags = new MatroskaFileTags(ioDW.getFilePointer());
+ tags.writeTags(ioDW);
cluster = new MatroskaCluster();
cluster.setLimitParameters(5000, 128 * 1024);
metaSeek.addIndexedElement(MatroskaDocTypes.Cluster.getType(), ioDW.getFilePointer());
@@ -116,6 +120,11 @@ void writeTracks()
tracks.update(ioDW);
}
+ void writeTags()
+ {
+ tags.update(ioDW);
+ }
+
public long getTimecodeScale()
{
return segmentInfoElem.getTimecodeScale();
@@ -156,6 +165,16 @@ public void addTrack(final MatroskaFileTrack track)
tracks.addTrack(track);
}
+ /**
+ * Adds a tag to the file. You may add tags at any time before close()ing.
+ *
+ * @param tag
+ */
+ public void addTag(final MatroskaFileTagEntry tag)
+ {
+ tags.addTag(tag);
+ }
+
/**
* Adds the silent track notation for the given track to subsequent clusters, note that this has little effect on most players
*/
@@ -207,5 +226,6 @@ public void close()
metaSeek.update(ioDW);
segmentInfoElem.update(ioDW);
tracks.update(ioDW);
+ tags.update(ioDW);
}
}
From 26a087eabbdc2d59cf0945d71e442e5d4d182cab Mon Sep 17 00:00:00 2001
From: ddsouz
Date: Thu, 17 Mar 2016 08:32:48 -0700
Subject: [PATCH 02/42] Reverting version back to 2.2 for Simple Tags.
---
build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/build.gradle b/build.gradle
index 7f397fb..eb24e74 100644
--- a/build.gradle
+++ b/build.gradle
@@ -68,7 +68,7 @@ publishing {
}
}
-project.version = '2.3.0'
+project.version = '2.2'
bundle {
instructions << [
From 40b5ce24d79f9701ebcd4fff77991ccd95fc52d6 Mon Sep 17 00:00:00 2001
From: ddsouz
Date: Thu, 17 Mar 2016 09:56:34 -0700
Subject: [PATCH 03/42] Modifying unit test to add a Simple Tag.
---
.../java/org/ebml/matroska/MatroskaFileWriterTest.java | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/src/test/java/org/ebml/matroska/MatroskaFileWriterTest.java b/src/test/java/org/ebml/matroska/MatroskaFileWriterTest.java
index a023104..2c90275 100644
--- a/src/test/java/org/ebml/matroska/MatroskaFileWriterTest.java
+++ b/src/test/java/org/ebml/matroska/MatroskaFileWriterTest.java
@@ -28,6 +28,7 @@ public class MatroskaFileWriterTest
private File destination;
private FileDataWriter ioDW;
private MatroskaFileTrack testTrack;
+ private MatroskaFileTagEntry testTag;
private int timecode = 1337;
@Before
@@ -40,6 +41,11 @@ public void setUp() throws Exception
testTrack.setTrackType(TrackType.SUBTITLE);
testTrack.setCodecID("some subtitle codec");
testTrack.setDefaultDuration(33);
+ MatroskaFileSimpleTag simpleTag = new MatroskaFileSimpleTag();
+ simpleTag.setName("TITLE");
+ simpleTag.setValue("Canon in D");
+ testTag = new MatroskaFileTagEntry();
+ testTag.addSimpleTag(simpleTag);
}
@After
@@ -55,6 +61,7 @@ public void testWrite() throws FileNotFoundException, IOException
final MatroskaFileWriter writer = new MatroskaFileWriter(ioDW);
writer.addTrack(testTrack);
writer.addFrame(generateFrame("I know a song...", 42));
+ writer.addTag(testTag);
writer.close();
final FileDataSource inputDataSource = new FileDataSource(destination.getPath());
From 63fbcc53fb20637cae4d495a20f178f28f542fcd Mon Sep 17 00:00:00 2001
From: goku3989
Date: Thu, 17 Mar 2016 14:47:08 -0600
Subject: [PATCH 04/42] Changing the default lacing mode to NONE.
---
src/main/java/org/ebml/matroska/MatroskaSimpleBlock.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/main/java/org/ebml/matroska/MatroskaSimpleBlock.java b/src/main/java/org/ebml/matroska/MatroskaSimpleBlock.java
index 4ecf7bf..0c4408c 100644
--- a/src/main/java/org/ebml/matroska/MatroskaSimpleBlock.java
+++ b/src/main/java/org/ebml/matroska/MatroskaSimpleBlock.java
@@ -23,7 +23,7 @@ class MatroskaSimpleBlock
private int trackNumber = 0;
private short timecode = 0;
private boolean keyFrame = true;
- private MatroskaLaceMode laceMode = MatroskaLaceMode.EBML;
+ private MatroskaLaceMode laceMode = MatroskaLaceMode.NONE;
private boolean invisible = false;
private boolean discardable = false;
private final List frames = new ArrayList<>();
From 3e89be1c1a09f97e18a6f462cb4b4b7b13dc67e5 Mon Sep 17 00:00:00 2001
From: goku3989
Date: Thu, 26 May 2016 16:01:03 -0600
Subject: [PATCH 05/42] Fixing some issues with Matroska standards compliance
(as reported by mkvalidator) in how 'Cues' and 'Tags' are handled. Also,
fixing an issue where the wrong file position was being used in the "Meta
Seek" headers for the "Cues" section.
---
gradlew | 0
.../org/ebml/matroska/MatroskaFileCues.java | 56 ++++++++++---------
.../ebml/matroska/MatroskaFileTagEntry.java | 11 ++--
3 files changed, 38 insertions(+), 29 deletions(-)
mode change 100755 => 100644 gradlew
diff --git a/gradlew b/gradlew
old mode 100755
new mode 100644
diff --git a/src/main/java/org/ebml/matroska/MatroskaFileCues.java b/src/main/java/org/ebml/matroska/MatroskaFileCues.java
index f7dc5b1..9e17e20 100644
--- a/src/main/java/org/ebml/matroska/MatroskaFileCues.java
+++ b/src/main/java/org/ebml/matroska/MatroskaFileCues.java
@@ -22,35 +22,41 @@ public MatroskaFileCues(long endOfEbmlHeaderBytePosition)
public void addCue(long positionInFile, long timecodeOfCluster, Collection clusterTrackNumbers)
{
- LOG.debug("Adding matroska cue to cues element at position [{}], using timecode [{}], for track numbers [{}]", positionInFile, timecodeOfCluster, clusterTrackNumbers);
-
- UnsignedIntegerElement cueTime = MatroskaDocTypes.CueTime.getInstance();
- cueTime.setValue(timecodeOfCluster);
- MasterElement cuePoint = MatroskaDocTypes.CuePoint.getInstance();
- MasterElement cueTrackPositions = createCueTrackPositions(positionInFile, clusterTrackNumbers);
-
- cues.addChildElement(cuePoint);
- cuePoint.addChildElement(cueTime);
- cuePoint.addChildElement(cueTrackPositions);
-
- LOG.debug("Finished adding matroska cue to cues element");
+ if (!clusterTrackNumbers.isEmpty())
+ {
+ LOG.debug("Adding matroska cue to cues element at position [{}], using timecode [{}], for track numbers [{}]", positionInFile, timecodeOfCluster, clusterTrackNumbers);
+
+ UnsignedIntegerElement cueTime = MatroskaDocTypes.CueTime.getInstance();
+ cueTime.setValue(timecodeOfCluster);
+ MasterElement cuePoint = MatroskaDocTypes.CuePoint.getInstance();
+ cuePoint.addChildElement(cueTime);
+ for (int trackNumber : clusterTrackNumbers)
+ {
+ MasterElement cueTrackPositions = createCueTrackPositions(positionInFile, trackNumber);
+ cuePoint.addChildElement(cueTrackPositions);
+ }
+ cues.addChildElement(cuePoint);
+
+ LOG.debug("Finished adding matroska cue to cues element");
+ }
+ else
+ {
+ LOG.debug("No track numbers specified. Not adding Cue.");
+ }
}
- private MasterElement createCueTrackPositions(final long positionInFile, final Collection trackNumbers)
+ private MasterElement createCueTrackPositions(long positionInFile, int trackNumber)
{
MasterElement cueTrackPositions = MatroskaDocTypes.CueTrackPositions.getInstance();
- for (Integer trackNumber : trackNumbers)
- {
- UnsignedIntegerElement cueTrack = MatroskaDocTypes.CueTrack.getInstance();
- cueTrack.setValue(trackNumber);
-
- UnsignedIntegerElement cueClusterPosition = MatroskaDocTypes.CueClusterPosition.getInstance();
- cueClusterPosition.setValue(getPositionRelativeToSegmentEbmlElement(positionInFile));
-
- cueTrackPositions.addChildElement(cueTrack);
- cueTrackPositions.addChildElement(cueClusterPosition);
- }
+ UnsignedIntegerElement cueTrack = MatroskaDocTypes.CueTrack.getInstance();
+ cueTrack.setValue(trackNumber);
+ cueTrackPositions.addChildElement(cueTrack);
+
+ UnsignedIntegerElement cueClusterPosition = MatroskaDocTypes.CueClusterPosition.getInstance();
+ cueClusterPosition.setValue(getPositionRelativeToSegmentEbmlElement(positionInFile));
+ cueTrackPositions.addChildElement(cueClusterPosition);
+
return cueTrackPositions;
}
@@ -61,7 +67,7 @@ public Element write(DataWriter ioDW, MatroskaFileMetaSeek metaSeek)
long numberOfBytesInCueData = cues.writeElement(ioDW);
LOG.debug("Done writing matroska cues, number of bytes was [{}]", numberOfBytesInCueData);
- metaSeek.addIndexedElement(cues, getPositionRelativeToSegmentEbmlElement(currentBytePositionInFile));
+ metaSeek.addIndexedElement(cues, currentBytePositionInFile);
return cues;
}
diff --git a/src/main/java/org/ebml/matroska/MatroskaFileTagEntry.java b/src/main/java/org/ebml/matroska/MatroskaFileTagEntry.java
index b6cf735..4f585a6 100644
--- a/src/main/java/org/ebml/matroska/MatroskaFileTagEntry.java
+++ b/src/main/java/org/ebml/matroska/MatroskaFileTagEntry.java
@@ -40,16 +40,19 @@ public void addSimpleTag(final MatroskaFileSimpleTag simpleTag)
Element toElement()
{
- final MasterElement tagEntryElem = MatroskaDocTypes.Tag.getInstance();
+ MasterElement tagEntryElem = MatroskaDocTypes.Tag.getInstance();
+ MasterElement targetsEntryElem = MatroskaDocTypes.Targets.getInstance();
+ tagEntryElem.addChildElement(targetsEntryElem);
+
for (MatroskaFileSimpleTag simpleTag : simpleTags)
{
- final MasterElement simpleTagEntryElem = MatroskaDocTypes.SimpleTag.getInstance();
+ MasterElement simpleTagEntryElem = MatroskaDocTypes.SimpleTag.getInstance();
- final UTF8StringElement simpleTagNameElem = MatroskaDocTypes.TagName.getInstance();
+ UTF8StringElement simpleTagNameElem = MatroskaDocTypes.TagName.getInstance();
simpleTagNameElem.setValue(simpleTag.getName());
- final UTF8StringElement simpleTagStringElem = MatroskaDocTypes.TagString.getInstance();
+ UTF8StringElement simpleTagStringElem = MatroskaDocTypes.TagString.getInstance();
simpleTagStringElem.setValue(simpleTag.getValue());
simpleTagEntryElem.addChildElement(simpleTagNameElem);
From 657ea8427096a321c6239a73fff12c24bd566a83 Mon Sep 17 00:00:00 2001
From: goku3989
Date: Thu, 2 Jun 2016 11:40:09 -0600
Subject: [PATCH 06/42] Updating MatroskaSimpleBlock to set the 'discardable'
header bit to true for non-key frames and false otherwise.
---
src/main/java/org/ebml/matroska/MatroskaBlock.java | 4 ----
src/main/java/org/ebml/matroska/MatroskaSimpleBlock.java | 9 ++++-----
2 files changed, 4 insertions(+), 9 deletions(-)
diff --git a/src/main/java/org/ebml/matroska/MatroskaBlock.java b/src/main/java/org/ebml/matroska/MatroskaBlock.java
index 7899283..adc3b9a 100644
--- a/src/main/java/org/ebml/matroska/MatroskaBlock.java
+++ b/src/main/java/org/ebml/matroska/MatroskaBlock.java
@@ -44,10 +44,6 @@ public MatroskaBlock(final ByteBuffer data)
this.data = data;
}
- // public void readData(DataSource source) {
- // parseBlock();
- // }
-
public void parseBlock()
{
int index = 0;
diff --git a/src/main/java/org/ebml/matroska/MatroskaSimpleBlock.java b/src/main/java/org/ebml/matroska/MatroskaSimpleBlock.java
index 0c4408c..8873394 100644
--- a/src/main/java/org/ebml/matroska/MatroskaSimpleBlock.java
+++ b/src/main/java/org/ebml/matroska/MatroskaSimpleBlock.java
@@ -36,11 +36,6 @@ public MatroskaSimpleBlock()
}
- public MatroskaSimpleBlock(final long duration)
- {
-
- }
-
static MatroskaSimpleBlock fromElement(final Element level3, final DataSource ioDS, final EBMLReader reader)
{
// TODO: make this work.
@@ -217,6 +212,7 @@ public boolean isDiscardable()
public void setDiscardable(final boolean discardable)
{
+ LOG.trace("Setting discardable to [{}] for SimpleBlock", discardable);
this.discardable = discardable;
}
@@ -225,6 +221,8 @@ public boolean addFrame(final MatroskaFileFrame frame)
LOG.trace("Adding frame {}", frame.getData().remaining());
setTimecode(frame.getTimecode());
setTrackNumber(frame.getTrackNo());
+ setKeyFrame(isKeyFrame() && frame.isKeyFrame());
+ setDiscardable(!frame.isKeyFrame());
totalSize += frame.getData().remaining();
frames.add(frame);
if (frame.getDuration() != Long.MIN_VALUE)
@@ -259,6 +257,7 @@ public boolean isKeyFrame()
public void setKeyFrame(final boolean keyFrame)
{
+ LOG.trace("Setting keyFrame to [{}] for SimpleBlock", keyFrame);
this.keyFrame = keyFrame;
}
From fd81f7c8e3b208190866769ee524d1c29c0af030 Mon Sep 17 00:00:00 2001
From: Michael Jameson
Date: Fri, 23 Sep 2016 09:30:10 -0700
Subject: [PATCH 07/42] Fixing thread saftey issue with ProtoType type buffer
---
src/main/java/org/ebml/ProtoType.java | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/src/main/java/org/ebml/ProtoType.java b/src/main/java/org/ebml/ProtoType.java
index e2a6406..a28e539 100644
--- a/src/main/java/org/ebml/ProtoType.java
+++ b/src/main/java/org/ebml/ProtoType.java
@@ -10,7 +10,7 @@ public class ProtoType
{
private static final Logger LOG = LoggerFactory.getLogger(ProtoType.class);
private static final HashMap> CLASS_MAP = new HashMap<>();
- Class clazz;
+ private final Class clazz;
private final ByteBuffer type;
private final String name;
@@ -64,7 +64,6 @@ public int getLevel()
public ByteBuffer getType()
{
- return type;
+ return type.asReadOnlyBuffer();
}
-
}
From 5c2c01e01ef99e957b1ea734b13f630b0c5483ae Mon Sep 17 00:00:00 2001
From: Michael Jameson
Date: Fri, 23 Sep 2016 09:40:23 -0700
Subject: [PATCH 08/42] Fixing the fix. Apparently we aren't ready for
read-only buffers.
---
src/main/java/org/ebml/ProtoType.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/main/java/org/ebml/ProtoType.java b/src/main/java/org/ebml/ProtoType.java
index a28e539..fae0128 100644
--- a/src/main/java/org/ebml/ProtoType.java
+++ b/src/main/java/org/ebml/ProtoType.java
@@ -64,6 +64,6 @@ public int getLevel()
public ByteBuffer getType()
{
- return type.asReadOnlyBuffer();
+ return type.duplicate();
}
}
From 63502ed15a50218c8cde542e9a4267a5685b7a08 Mon Sep 17 00:00:00 2001
From: Michael Jameson
Date: Fri, 23 Sep 2016 10:08:48 -0700
Subject: [PATCH 09/42] Now actually using readOnlyBuffer for ProtoType type
buffers
---
src/main/java/org/ebml/BinaryElement.java | 3 ++-
src/main/java/org/ebml/Element.java | 9 ++-------
src/main/java/org/ebml/MasterElement.java | 10 ----------
src/main/java/org/ebml/ProtoType.java | 2 +-
src/main/java/org/ebml/matroska/MatroskaSegment.java | 3 ++-
5 files changed, 7 insertions(+), 20 deletions(-)
diff --git a/src/main/java/org/ebml/BinaryElement.java b/src/main/java/org/ebml/BinaryElement.java
index f55100b..0e0d142 100644
--- a/src/main/java/org/ebml/BinaryElement.java
+++ b/src/main/java/org/ebml/BinaryElement.java
@@ -42,7 +42,8 @@ public class BinaryElement extends Element
*/
public BinaryElement(final byte[] type)
{
- super(type);
+ super();
+ setType(type);
}
public BinaryElement()
diff --git a/src/main/java/org/ebml/Element.java b/src/main/java/org/ebml/Element.java
index c05a1d8..85515fa 100644
--- a/src/main/java/org/ebml/Element.java
+++ b/src/main/java/org/ebml/Element.java
@@ -52,11 +52,6 @@ public class Element
private Long headersSize = null;
/** Creates a new instance of Element */
- public Element(final byte[] type)
- {
- this.type = ByteBuffer.wrap(type);
- }
-
public Element()
{
}
@@ -130,7 +125,7 @@ public long writeData(final DataWriter writer)
data.mark();
try
{
- LOG.trace("Writing data {} bytes of {}", data.remaining(), EBMLReader.bytesToHex(data.array()));
+ LOG.trace("Writing data {} bytes", data.remaining());
return writer.write(data);
}
finally
@@ -207,7 +202,7 @@ public long getTotalSize()
}
else
{
- totalSize += getType().array().length;
+ totalSize += getType().remaining();
totalSize += Element.codedSizeLength(getSize(), 0);
}
totalSize += getSize();
diff --git a/src/main/java/org/ebml/MasterElement.java b/src/main/java/org/ebml/MasterElement.java
index 84a9cff..8704c29 100644
--- a/src/main/java/org/ebml/MasterElement.java
+++ b/src/main/java/org/ebml/MasterElement.java
@@ -29,16 +29,6 @@ public class MasterElement extends Element
protected long usedSize = 0;
protected ArrayList children = new ArrayList<>();
- public MasterElement(final byte[] type)
- {
- super(type);
- }
-
- public MasterElement()
- {
- super();
- }
-
public Element readNextChild(final EBMLReader reader)
{
if (usedSize >= this.getSize())
diff --git a/src/main/java/org/ebml/ProtoType.java b/src/main/java/org/ebml/ProtoType.java
index fae0128..a28e539 100644
--- a/src/main/java/org/ebml/ProtoType.java
+++ b/src/main/java/org/ebml/ProtoType.java
@@ -64,6 +64,6 @@ public int getLevel()
public ByteBuffer getType()
{
- return type.duplicate();
+ return type.asReadOnlyBuffer();
}
}
diff --git a/src/main/java/org/ebml/matroska/MatroskaSegment.java b/src/main/java/org/ebml/matroska/MatroskaSegment.java
index 1f3b249..4ba1094 100644
--- a/src/main/java/org/ebml/matroska/MatroskaSegment.java
+++ b/src/main/java/org/ebml/matroska/MatroskaSegment.java
@@ -34,7 +34,8 @@ public class MatroskaSegment extends MasterElement
public MatroskaSegment()
{
- super(MatroskaDocTypes.Segment.getType().array());
+ super();
+ setType(MatroskaDocTypes.Segment.getType());
}
/**
From 2053f6898b872f4b2eb85c314ab900700115e76b Mon Sep 17 00:00:00 2001
From: Michael Jameson
Date: Fri, 23 Sep 2016 10:45:18 -0700
Subject: [PATCH 10/42] More refactors to use read-only buffers
---
src/main/java/org/ebml/EBMLReader.java | 27 +++++++-------
src/main/java/org/ebml/Element.java | 37 +++++++++++++------
src/main/java/org/ebml/FloatElement.java | 25 +++++--------
src/main/java/org/ebml/MasterElement.java | 6 +--
.../java/org/ebml/SignedIntegerElement.java | 8 +---
src/main/java/org/ebml/StringElement.java | 2 +-
.../java/org/ebml/UnsignedIntegerElement.java | 4 +-
.../org/ebml/matroska/MatroskaFileCues.java | 20 +++++-----
.../java/org/ebml/matroska/VoidElement.java | 4 +-
src/test/java/org/ebml/MockSource.java | 6 +--
10 files changed, 72 insertions(+), 67 deletions(-)
diff --git a/src/main/java/org/ebml/EBMLReader.java b/src/main/java/org/ebml/EBMLReader.java
index 106478a..7232c20 100644
--- a/src/main/java/org/ebml/EBMLReader.java
+++ b/src/main/java/org/ebml/EBMLReader.java
@@ -85,12 +85,13 @@ public EBMLReader(final DataSource source)
this.source = source;
}
- public static String bytesToHex(final byte[] bytes)
+ public static String bytesToHex(final ByteBuffer data)
{
- final char[] hexChars = new char[bytes.length * 2];
- for (int j = 0; j < bytes.length; j++)
+ ByteBuffer read = data.asReadOnlyBuffer();
+ final char[] hexChars = new char[data.remaining() * 2];
+ for (int j = 0; read.hasRemaining(); j++)
{
- final int v = bytes[j] & 0xFF;
+ final int v = read.get() & 0xFF;
hexChars[j * 2] = HEX_ARRAY[v >>> 4];
hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F];
}
@@ -122,14 +123,14 @@ public Element readNextElement()
if (elementSize == 0)
{
// Zero sized element is valid
- LOG.error("Invalid element size for {}", elem.typeInfo.getName());
+ LOG.error("Invalid element size for {}", elem.getElementType().getName());
}
final long end = source.getFilePointer();
// Set it's size
elem.setSize(elementSize);
elem.setHeadersSize(end - position);
- LOG.trace("Read element {} with size {}", elem.typeInfo.getName(), elem.getTotalSize());
+ LOG.trace("Read element {} with size {}", elem.getElementType().getName(), elem.getTotalSize());
// Setup a buffer for it's data
// byte[] elementData = new byte[(int)elementSize];
@@ -239,17 +240,16 @@ public static long parseEBMLCode(final ByteBuffer data)
{
return 0;
}
- data.mark();
+ ByteBuffer read = data.asReadOnlyBuffer();
// Put this into a long
long size = 0;
- for (int i = data.remaining() - 1; i >= 0; i--)
+ for (int i = read.remaining() - 1; i >= 0; i--)
{
- final long n = data.get() & 0xFF;
+ final long n = read.get() & 0xFF;
size = size | (n << (8 * i));
}
- data.reset();
- LOG.trace("Parsed ebml code {} as {}", bytesToHex(data.array()), size);
+ LOG.trace("Parsed ebml code {} as {}", bytesToHex(data), size);
return size;
}
@@ -293,8 +293,9 @@ public static long readEBMLCode(final ByteBuffer source)
*/
public static long readSignedEBMLCode(final ByteBuffer source)
{
+ ByteBuffer read = source.asReadOnlyBuffer();
// Begin loop with byte set to newly read byte.
- final byte firstByte = source.get();
+ final byte firstByte = read.get();
final int numBytes = readEBMLCodeSize(firstByte);
if (numBytes == 0)
{
@@ -311,7 +312,7 @@ public static long readSignedEBMLCode(final ByteBuffer source)
if (numBytes > 1)
{
// Read the rest of the size.
- data.put(source);
+ data.put(read);
}
data.flip();
// Put this into a long
diff --git a/src/main/java/org/ebml/Element.java b/src/main/java/org/ebml/Element.java
index 85515fa..0c83a82 100644
--- a/src/main/java/org/ebml/Element.java
+++ b/src/main/java/org/ebml/Element.java
@@ -43,12 +43,12 @@ public class Element
protected static final Logger LOG = LoggerFactory.getLogger(Element.class);
private static int minSizeLength = 0;
- protected Element parent;
- protected ProtoType> typeInfo;
- protected ByteBuffer type;
- protected long size = 0;
- protected ByteBuffer data = null;
- protected boolean dataRead = false;
+ private Element parent;
+ private ProtoType> typeInfo;
+ private ByteBuffer type;
+ private long size = 0;
+ private ByteBuffer data = null;
+ private boolean dataRead = false;
private Long headersSize = null;
/** Creates a new instance of Element */
@@ -108,7 +108,7 @@ public long writeHeaderData(final DataWriter writer)
buf.put(getType());
buf.put(encodedSize);
buf.flip();
- LOG.trace("Writing out header {}, {}", buf.remaining(), EBMLReader.bytesToHex(buf.array()));
+ LOG.trace("Writing out header {}, {}", buf.remaining(), EBMLReader.bytesToHex(buf));
writer.write(buf);
return len;
}
@@ -120,7 +120,7 @@ public long writeData(final DataWriter writer)
{
if (data == null)
{
- throw new NullPointerException(String.format("No data to write: %s : %s", typeInfo.getName(), Arrays.toString(this.type.array())));
+ throw new NullPointerException(String.format("No data to write: %s : %s", typeInfo.getName(), EBMLReader.bytesToHex(this.type)));
}
data.mark();
try
@@ -142,7 +142,22 @@ public long writeData(final DataWriter writer)
*/
public ByteBuffer getData()
{
- return this.data.duplicate();
+ if (data != null)
+ {
+ return this.data.asReadOnlyBuffer();
+ }
+ return ByteBuffer.allocate(0);
+ }
+
+ public byte[] getDataArray()
+ {
+ if (data != null)
+ {
+ byte[] bytes = new byte[data.remaining()];
+ data.slice().get(bytes);
+ return bytes;
+ }
+ return new byte[0];
}
/**
@@ -217,7 +232,7 @@ public long getTotalSize()
*/
public ByteBuffer getType()
{
- return type.duplicate();
+ return type.asReadOnlyBuffer();
}
/**
@@ -312,7 +327,7 @@ public static byte[] makeEbmlCodedSize(final long size, int minSizeLen)
}
// The first size bits should be clear, otherwise we have an error in the size determination.
ret[0] |= 0x80 >> (len - 1);
- LOG.trace("Ebml coded size {} for {}", EBMLReader.bytesToHex(ret), size);
+ LOG.trace("Ebml coded size {} for {}", EBMLReader.bytesToHex(ByteBuffer.wrap(ret)), size);
return ret;
}
diff --git a/src/main/java/org/ebml/FloatElement.java b/src/main/java/org/ebml/FloatElement.java
index 15239ee..5a2fa19 100644
--- a/src/main/java/org/ebml/FloatElement.java
+++ b/src/main/java/org/ebml/FloatElement.java
@@ -65,25 +65,18 @@ public void setValue(final double value)
*/
public double getValue()
{
- data.mark();
- try
+ long elemSize = getSize();
+ if (elemSize == 4)
{
- if (size == 4)
- {
- return data.getFloat();
- }
- else if (size == 8)
- {
- return data.getDouble();
- }
- else
- {
- throw new ArithmeticException("80-bit floats are not supported");
- }
+ return getData().getFloat();
}
- finally
+ else if (elemSize == 8)
{
- data.reset();
+ return getData().getDouble();
+ }
+ else
+ {
+ throw new ArithmeticException("80-bit floats are not supported");
}
}
}
diff --git a/src/main/java/org/ebml/MasterElement.java b/src/main/java/org/ebml/MasterElement.java
index 8704c29..f618b16 100644
--- a/src/main/java/org/ebml/MasterElement.java
+++ b/src/main/java/org/ebml/MasterElement.java
@@ -48,7 +48,7 @@ public Element readNextChild(final EBMLReader reader)
usedSize += elem.getTotalSize();
- LOG.trace("Read element {} of size {}: {} remaining", elem.typeInfo.getName(), elem.getTotalSize(), size - usedSize);
+ LOG.trace("Read element {} of size {}: {} remaining", elem.getElementType().getName(), elem.getTotalSize(), getSize() - usedSize);
return elem;
}
@@ -57,7 +57,7 @@ public Element readNextChild(final EBMLReader reader)
public void skipData(final DataSource source)
{
// Skip the child elements
- source.skip(size - usedSize);
+ source.skip(getSize() - usedSize);
}
@Override
@@ -75,6 +75,6 @@ public long writeData(final DataWriter writer)
public void addChildElement(final Element elem)
{
children.add(elem);
- size += elem.getTotalSize();
+ super.setSize(getSize() + elem.getTotalSize());
}
}
diff --git a/src/main/java/org/ebml/SignedIntegerElement.java b/src/main/java/org/ebml/SignedIntegerElement.java
index ce2ac5d..12e9e6a 100644
--- a/src/main/java/org/ebml/SignedIntegerElement.java
+++ b/src/main/java/org/ebml/SignedIntegerElement.java
@@ -47,16 +47,12 @@ public SignedIntegerElement()
public void setValue(final long value)
{
- // System.out.println(Long.toHexString(value));
setData(ByteBuffer.wrap(packInt(value)));
- /*
- * for (int i = 0; i < data.length; i++) { System.out.print(Integer.toHexString(data[i]) + ", "); } System.out.print("\n");
- */
}
public long getValue()
{
- final byte[] dataArray = data.array();
+ final byte[] dataArray = getDataArray();
long l = 0;
long tmp = 0;
l |= ((long) dataArray[0] << (56 - ((8 - dataArray.length) * 8)));
@@ -66,8 +62,6 @@ public long getValue()
tmp >>>= 56 - (8 * (i - 1));
l |= tmp;
}
- // System.out.println(Long.toHexString(l));
return l;
}
-
}
diff --git a/src/main/java/org/ebml/StringElement.java b/src/main/java/org/ebml/StringElement.java
index 4210e98..eefb93e 100644
--- a/src/main/java/org/ebml/StringElement.java
+++ b/src/main/java/org/ebml/StringElement.java
@@ -59,7 +59,7 @@ public StringElement(final Charset encoding)
public String getValue()
{
- return new String(data.array(), charset);
+ return new String(getDataArray(), charset);
}
public void setValue(final String value)
diff --git a/src/main/java/org/ebml/UnsignedIntegerElement.java b/src/main/java/org/ebml/UnsignedIntegerElement.java
index 3c11148..5e0fff2 100644
--- a/src/main/java/org/ebml/UnsignedIntegerElement.java
+++ b/src/main/java/org/ebml/UnsignedIntegerElement.java
@@ -47,12 +47,12 @@ public UnsignedIntegerElement()
public void setValue(final long value)
{
final ByteBuffer buf = ByteBuffer.wrap(packIntUnsigned(value));
- LOG.trace("Setting value {} to {}", value, EBMLReader.bytesToHex(buf.array()));
+ LOG.trace("Setting value {} to {}", value, EBMLReader.bytesToHex(buf));
setData(buf);
}
public long getValue()
{
- return EBMLReader.parseEBMLCode(data.duplicate());
+ return EBMLReader.parseEBMLCode(getData());
}
}
diff --git a/src/main/java/org/ebml/matroska/MatroskaFileCues.java b/src/main/java/org/ebml/matroska/MatroskaFileCues.java
index 9e17e20..c06d547 100644
--- a/src/main/java/org/ebml/matroska/MatroskaFileCues.java
+++ b/src/main/java/org/ebml/matroska/MatroskaFileCues.java
@@ -14,7 +14,7 @@ public class MatroskaFileCues
private static final Logger LOG = LoggerFactory.getLogger(MatroskaFileCues.class);
private MasterElement cues = MatroskaDocTypes.Cues.getInstance();
private long endOfEbmlHeaderBytePosition;
-
+
public MatroskaFileCues(long endOfEbmlHeaderBytePosition)
{
this.endOfEbmlHeaderBytePosition = endOfEbmlHeaderBytePosition;
@@ -24,8 +24,11 @@ public void addCue(long positionInFile, long timecodeOfCluster, Collection
Date: Tue, 29 Nov 2016 16:58:13 -0800
Subject: [PATCH 11/42] Fixing issue with simple block flags caused by bit
numbering of BitSet
---
gradlew | 0
src/main/java/org/ebml/BitSet.java | 71 +++++++++++++++++++
.../ebml/matroska/MatroskaSimpleBlock.java | 4 +-
3 files changed, 73 insertions(+), 2 deletions(-)
mode change 100644 => 100755 gradlew
create mode 100644 src/main/java/org/ebml/BitSet.java
diff --git a/gradlew b/gradlew
old mode 100644
new mode 100755
diff --git a/src/main/java/org/ebml/BitSet.java b/src/main/java/org/ebml/BitSet.java
new file mode 100644
index 0000000..f4536e9
--- /dev/null
+++ b/src/main/java/org/ebml/BitSet.java
@@ -0,0 +1,71 @@
+package org.ebml;
+
+import java.util.Arrays;
+
+/**
+ * Partially re-implements {@link java.util.BitSet} because Java's BitSet
+ * is LSB0 instead of MSB0 and its too much of a pain to translate
+ */
+public class BitSet
+{
+ private static final int[] MASKS = new int[] {0x80,
+ 0x40,
+ 0x20,
+ 0x10,
+ 0x08,
+ 0x04,
+ 0x02,
+ 0x01 };
+ private static final int[] NOT_MASKS = new int[] {0x7F,
+ 0xBF,
+ 0xDF,
+ 0xEF,
+ 0xF7,
+ 0xFB,
+ 0xFD,
+ 0xFE };
+ private byte[] bitSet;
+
+ public BitSet(int bits)
+ {
+ int bytes = (bits % 8 == 0 ? 0 : 1) + bits / 8;
+ bitSet = new byte[bytes];
+ }
+
+ public BitSet set(int bitNumber)
+ {
+ return set(bitNumber, true);
+ }
+
+ public BitSet unset(int bitNumber)
+ {
+ return set(bitNumber, false);
+ }
+
+ public BitSet set(int bitNumber, boolean value)
+ {
+ int bitInByte = bitNumber % 8;
+ int byteNumber = bitNumber / 8;
+ if (byteNumber > bitSet.length)
+ {
+ throw new IndexOutOfBoundsException("No such bit in set");
+ }
+ if (value)
+ {
+ bitSet[byteNumber] |= MASKS[bitInByte];
+ }
+ else
+ {
+ bitSet[byteNumber] &= NOT_MASKS[bitInByte];
+ }
+ return this;
+ }
+
+ /**
+ * @return
+ */
+ public byte[] toByteArray()
+ {
+ return Arrays.copyOf(bitSet, bitSet.length);
+ }
+}
diff --git a/src/main/java/org/ebml/matroska/MatroskaSimpleBlock.java b/src/main/java/org/ebml/matroska/MatroskaSimpleBlock.java
index 8873394..4ef0359 100644
--- a/src/main/java/org/ebml/matroska/MatroskaSimpleBlock.java
+++ b/src/main/java/org/ebml/matroska/MatroskaSimpleBlock.java
@@ -2,10 +2,10 @@
import java.nio.ByteBuffer;
import java.util.ArrayList;
-import java.util.BitSet;
import java.util.List;
import org.ebml.BinaryElement;
+import org.ebml.BitSet;
import org.ebml.EBMLReader;
import org.ebml.Element;
import org.ebml.MasterElement;
@@ -119,7 +119,7 @@ private ByteBuffer createInnerData()
{
buf.put(sizes);
}
- for (final MatroskaFileFrame frame: frames)
+ for (final MatroskaFileFrame frame : frames)
{
LOG.trace("Writing frame {}", frame.getData().remaining());
buf.put(frame.getData());
From 860bcd43dc150ce4f740d25983b2d06ba2dc754d Mon Sep 17 00:00:00 2001
From: Michael Jameson
Date: Wed, 30 Nov 2016 08:52:17 -0800
Subject: [PATCH 12/42] Adding BitSetTest
---
src/test/java/org/ebml/BitSetTest.java | 50 ++++++++++++++++++++++++++
1 file changed, 50 insertions(+)
create mode 100644 src/test/java/org/ebml/BitSetTest.java
diff --git a/src/test/java/org/ebml/BitSetTest.java b/src/test/java/org/ebml/BitSetTest.java
new file mode 100644
index 0000000..b4f048e
--- /dev/null
+++ b/src/test/java/org/ebml/BitSetTest.java
@@ -0,0 +1,50 @@
+package org.ebml;
+
+import java.util.Arrays;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Tests {@link BitSet}
+ */
+public class BitSetTest
+{
+ private static final Logger LOG = LoggerFactory.getLogger(BitSetTest.class);
+
+ private void testBits(int expectByte, int... bits)
+ {
+ LOG.info("Testing bits {}, expecting {}", Arrays.toString(bits), expectByte);
+ BitSet bitSet = new BitSet(8);
+ for (int bit : bits)
+ {
+ bitSet.set(bit);
+ }
+ Assert.assertEquals((byte) expectByte, bitSet.toByteArray()[0]);
+ }
+
+ @Test
+ public void testOneBit()
+ {
+ testBits(0x80, 0);
+ testBits(0x20, 2);
+ testBits(0x04, 5);
+ }
+
+ @Test
+ public void testTwoBits()
+ {
+ testBits(0x48, 1, 4);
+ testBits(0x12, 3, 6);
+ }
+
+ @Test
+ public void testManyBits()
+ {
+ testBits(0x58, 1, 3, 4);
+ testBits(0xF0, 0, 1, 2, 3);
+ testBits(0xFF, 0, 1, 2, 3, 4, 5, 6, 7);
+ }
+}
From 991b7fe062e23741e641bbb98ec97caad3577345 Mon Sep 17 00:00:00 2001
From: Michael Jameson
Date: Wed, 30 Nov 2016 08:56:15 -0800
Subject: [PATCH 13/42] Bumping version of JEBML in muxing app element
---
src/main/java/org/ebml/matroska/MatroskaSegmentInfo.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/main/java/org/ebml/matroska/MatroskaSegmentInfo.java b/src/main/java/org/ebml/matroska/MatroskaSegmentInfo.java
index d0523d0..6725686 100644
--- a/src/main/java/org/ebml/matroska/MatroskaSegmentInfo.java
+++ b/src/main/java/org/ebml/matroska/MatroskaSegmentInfo.java
@@ -34,7 +34,7 @@ public long writeElement(final DataWriter ioDW)
writingAppElem.setValue("Matroska File Writer v1.0");
final StringElement muxingAppElem = MatroskaDocTypes.MuxingApp.getInstance();
- muxingAppElem.setValue("JEBML v1.0");
+ muxingAppElem.setValue("JEBML v2.2");
final DateElement dateElem = MatroskaDocTypes.DateUTC.getInstance();
dateElem.setDate(segmentDate);
From e7c2920a43d94f8085cd3dba44f4da396fa2b45e Mon Sep 17 00:00:00 2001
From: Michael Jameson
Date: Wed, 30 Nov 2016 09:15:26 -0800
Subject: [PATCH 14/42] Adding missing mandatory tag child elements
---
.../ebml/matroska/MatroskaFileTagEntry.java | 22 ++++++++++++++-----
1 file changed, 17 insertions(+), 5 deletions(-)
diff --git a/src/main/java/org/ebml/matroska/MatroskaFileTagEntry.java b/src/main/java/org/ebml/matroska/MatroskaFileTagEntry.java
index 4f585a6..c2f9644 100644
--- a/src/main/java/org/ebml/matroska/MatroskaFileTagEntry.java
+++ b/src/main/java/org/ebml/matroska/MatroskaFileTagEntry.java
@@ -24,7 +24,9 @@
import org.ebml.Element;
import org.ebml.MasterElement;
+import org.ebml.StringElement;
import org.ebml.UTF8StringElement;
+import org.ebml.UnsignedIntegerElement;
public class MatroskaFileTagEntry
{
@@ -44,19 +46,29 @@ Element toElement()
MasterElement targetsEntryElem = MatroskaDocTypes.Targets.getInstance();
tagEntryElem.addChildElement(targetsEntryElem);
-
+
for (MatroskaFileSimpleTag simpleTag : simpleTags)
{
MasterElement simpleTagEntryElem = MatroskaDocTypes.SimpleTag.getInstance();
UTF8StringElement simpleTagNameElem = MatroskaDocTypes.TagName.getInstance();
simpleTagNameElem.setValue(simpleTag.getName());
+ simpleTagEntryElem.addChildElement(simpleTagNameElem);
- UTF8StringElement simpleTagStringElem = MatroskaDocTypes.TagString.getInstance();
- simpleTagStringElem.setValue(simpleTag.getValue());
+ if (simpleTag.getValue() != null)
+ {
+ UTF8StringElement simpleTagStringElem = MatroskaDocTypes.TagString.getInstance();
+ simpleTagStringElem.setValue(simpleTag.getValue());
+ simpleTagEntryElem.addChildElement(simpleTagStringElem);
+ }
- simpleTagEntryElem.addChildElement(simpleTagNameElem);
- simpleTagEntryElem.addChildElement(simpleTagStringElem);
+ StringElement lang = MatroskaDocTypes.TagLanguage.getInstance();
+ lang.setValue("eng");
+ simpleTagEntryElem.addChildElement(lang);
+
+ UnsignedIntegerElement tagDefault = MatroskaDocTypes.TagDefault.getInstance();
+ tagDefault.setValue(1);
+ simpleTagEntryElem.addChildElement(tagDefault);
tagEntryElem.addChildElement(simpleTagEntryElem);
}
From 58683e94147738cce862e24acb036170a5afe000 Mon Sep 17 00:00:00 2001
From: Denis Kokorin
Date: Mon, 10 Jul 2017 20:07:05 +0300
Subject: [PATCH 15/42] Add support for ColourSpace (#5)
* Add support for ColourSpace
(cherry picked from commit ca5f8cf)
* Add support for ColourSpace - ByteBuffer instead of byte[]
---
.../org/ebml/matroska/MatroskaFileTrack.java | 37 ++++++++++++++-----
1 file changed, 27 insertions(+), 10 deletions(-)
diff --git a/src/main/java/org/ebml/matroska/MatroskaFileTrack.java b/src/main/java/org/ebml/matroska/MatroskaFileTrack.java
index d6ccf30..1d6d4cf 100644
--- a/src/main/java/org/ebml/matroska/MatroskaFileTrack.java
+++ b/src/main/java/org/ebml/matroska/MatroskaFileTrack.java
@@ -19,20 +19,14 @@
*/
package org.ebml.matroska;
-import java.nio.ByteBuffer;
-import java.util.ArrayList;
-
-import org.ebml.BinaryElement;
-import org.ebml.EBMLReader;
-import org.ebml.Element;
-import org.ebml.FloatElement;
-import org.ebml.MasterElement;
-import org.ebml.StringElement;
-import org.ebml.UnsignedIntegerElement;
+import org.ebml.*;
import org.ebml.io.DataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+
/**
* Matroska Track Class
* Mandatory fields are:
@@ -114,6 +108,7 @@ public static class MatroskaVideoTrack
private short pixelHeight;
private short displayWidth = 0;
private short displayHeight = 0;
+ private ByteBuffer colourSpace = null;
public short getPixelWidth()
{
@@ -154,6 +149,14 @@ public void setDisplayHeight(final short displayHeight)
{
this.displayHeight = displayHeight;
}
+
+ public ByteBuffer getColourSpace() {
+ return colourSpace;
+ }
+
+ public void setColourSpace(ByteBuffer colourSpace) {
+ this.colourSpace = colourSpace;
+ }
}
private MatroskaVideoTrack video = null;
@@ -354,6 +357,11 @@ else if (level4.isType(MatroskaDocTypes.DisplayHeight.getType()))
level4.readData(ioDS);
track.video.setDisplayHeight((short) ((UnsignedIntegerElement) level4).getValue());
}
+ else if (level4.isType(MatroskaDocTypes.ColourSpace.getType()))
+ {
+ level4.readData(ioDS);
+ track.video.setColourSpace(((BinaryElement) level4).getData());
+ }
level4.skipData(ioDS);
level4 = ((MasterElement) level3).readNextChild(reader);
@@ -505,10 +513,19 @@ Element toElement()
final UnsignedIntegerElement trackVideoDisplayHeightElem = MatroskaDocTypes.DisplayHeight.getInstance();
trackVideoDisplayHeightElem.setValue(this.video.getDisplayHeight());
+ BinaryElement colourSpaceElem = null;
+ if (this.video.getColourSpace() != null) {
+ colourSpaceElem = MatroskaDocTypes.ColourSpace.getInstance();
+ colourSpaceElem.setData(this.video.getColourSpace());
+ }
+
trackVideoElem.addChildElement(trackVideoPixelWidthElem);
trackVideoElem.addChildElement(trackVideoPixelHeightElem);
trackVideoElem.addChildElement(trackVideoDisplayWidthElem);
trackVideoElem.addChildElement(trackVideoDisplayHeightElem);
+ if (colourSpaceElem != null) {
+ trackVideoElem.addChildElement(colourSpaceElem);
+ }
trackEntryElem.addChildElement(trackVideoElem);
}
From f346bca01e82705e9b1cbb2ecd75eb0d28e915a5 Mon Sep 17 00:00:00 2001
From: Denis Kokorin
Date: Tue, 11 Jul 2017 00:34:37 +0300
Subject: [PATCH 16/42] Nonseekable DataWriter Support (#4)
* Add support for non-seekable DataWriters.
(cherry picked from commit 8d454ec)
* Add support for non-seekable DataWriters.
Cues and meta seek are not written.
---
.../org/ebml/matroska/MatroskaFileWriter.java | 142 +++++++++++++++---
.../ebml/matroska/MatroskaFileWriterTest.java | 31 ++--
.../MatroskaUnseekableWriterTest.java | 24 +++
3 files changed, 163 insertions(+), 34 deletions(-)
create mode 100644 src/test/java/org/ebml/matroska/MatroskaUnseekableWriterTest.java
diff --git a/src/main/java/org/ebml/matroska/MatroskaFileWriter.java b/src/main/java/org/ebml/matroska/MatroskaFileWriter.java
index 6f5a408..2f0acb3 100644
--- a/src/main/java/org/ebml/matroska/MatroskaFileWriter.java
+++ b/src/main/java/org/ebml/matroska/MatroskaFileWriter.java
@@ -26,6 +26,9 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import java.util.ArrayList;
+import java.util.List;
+
/**
* Primary API entrypoint for writing Matroska files.
*/
@@ -35,12 +38,18 @@ public class MatroskaFileWriter
protected DataWriter ioDW;
- private final MatroskaFileMetaSeek metaSeek;
- private final MatroskaFileCues cueData;
- private final MatroskaCluster cluster;
- private final MatroskaSegmentInfo segmentInfoElem;
- private final MatroskaFileTracks tracks;
- private final MatroskaFileTags tags;
+ private MatroskaFileMetaSeek metaSeek;
+ private MatroskaFileCues cueData;
+ private MatroskaCluster cluster;
+ private MatroskaSegmentInfo segmentInfoElem;
+ private MatroskaFileTracks tracks;
+ private MatroskaFileTags tags;
+
+ private Long defferedTimecodeScale;
+ private Double defferedDuration;
+ private List defferedTracks;
+ private List defferedTags;
+ private boolean initialized = false;
/**
* @param outputDataWriter DataWriter to write out to.
@@ -48,24 +57,65 @@ public class MatroskaFileWriter
public MatroskaFileWriter(final DataWriter outputDataWriter)
{
ioDW = outputDataWriter;
+ }
+
+ void initialize() {
+ if (initialized) {
+ return;
+ }
+
writeEBMLHeader();
writeSegmentHeader();
+
long endOfSegmentHeader = ioDW.getFilePointer();
- metaSeek = new MatroskaFileMetaSeek(endOfSegmentHeader);
- cueData = new MatroskaFileCues(endOfSegmentHeader);
- metaSeek.write(ioDW);
+
+ if (ioDW.isSeekable()) {
+ metaSeek = new MatroskaFileMetaSeek(endOfSegmentHeader);
+ cueData = new MatroskaFileCues(endOfSegmentHeader);
+ metaSeek.write(ioDW);
+ }
+
segmentInfoElem = new MatroskaSegmentInfo(ioDW.getFilePointer());
- metaSeek.addIndexedElement(MatroskaDocTypes.Info.getType(), ioDW.getFilePointer());
+ if (metaSeek != null) {
+ metaSeek.addIndexedElement(MatroskaDocTypes.Info.getType(), ioDW.getFilePointer());
+ }
+ if (defferedTimecodeScale != null) {
+ segmentInfoElem.setTimecodeScale(defferedTimecodeScale);
+ }
+ if (defferedDuration != null) {
+ segmentInfoElem.setDuration(defferedDuration);
+ }
segmentInfoElem.writeElement(ioDW);
- metaSeek.addIndexedElement(MatroskaDocTypes.Tracks.getType(), ioDW.getFilePointer());
+
+ if (metaSeek != null) {
+ metaSeek.addIndexedElement(MatroskaDocTypes.Tracks.getType(), ioDW.getFilePointer());
+ }
tracks = new MatroskaFileTracks(ioDW.getFilePointer());
+ if (defferedTracks != null) {
+ for (MatroskaFileTrack track : defferedTracks) {
+ tracks.addTrack(track);
+ }
+ }
tracks.writeTracks(ioDW);
- metaSeek.addIndexedElement(MatroskaDocTypes.Tags.getType(), ioDW.getFilePointer());
+
+ if (metaSeek != null) {
+ metaSeek.addIndexedElement(MatroskaDocTypes.Tags.getType(), ioDW.getFilePointer());
+ }
tags = new MatroskaFileTags(ioDW.getFilePointer());
+ if (defferedTags != null) {
+ for (MatroskaFileTagEntry tag : defferedTags) {
+ tags.addTag(tag);
+ }
+ }
tags.writeTags(ioDW);
+
cluster = new MatroskaCluster();
cluster.setLimitParameters(5000, 128 * 1024);
- metaSeek.addIndexedElement(MatroskaDocTypes.Cluster.getType(), ioDW.getFilePointer());
+ if (metaSeek != null) {
+ metaSeek.addIndexedElement(MatroskaDocTypes.Cluster.getType(), ioDW.getFilePointer());
+ }
+
+ initialized = true;
}
void writeEBMLHeader()
@@ -137,6 +187,13 @@ public long getTimecodeScale()
*/
public void setTimecodeScale(final long timecodeScale)
{
+ if (initialized && !ioDW.isSeekable()) {
+ throw new UnsupportedOperationException("DataWriter isn't seekable, can't change timecodeScale after starting writing");
+ }
+ if (!initialized) {
+ defferedTimecodeScale = timecodeScale;
+ return;
+ }
segmentInfoElem.setTimecodeScale(timecodeScale);
}
@@ -152,26 +209,57 @@ public double getDuration()
*/
public void setDuration(final double duration)
{
+ if (initialized && !ioDW.isSeekable()) {
+ throw new UnsupportedOperationException("DataWriter isn't seekable, can't change duration after starting writing");
+ }
+ if (!initialized) {
+ defferedDuration = duration;
+ return;
+ }
segmentInfoElem.setDuration(duration);
}
/**
- * Adds a track to the file. You may add tracks at any time before close()ing, even after adding frames for the track.
+ * Adds a track to the file.
+ *
+ * You may add tracks at any time before close()ing, only if DataWriter is seekable.
+ * Otherwise you may add tracks only before frames.
*
* @param track
*/
public void addTrack(final MatroskaFileTrack track)
{
+ if (initialized && !ioDW.isSeekable()) {
+ throw new UnsupportedOperationException("DataWriter isn't seekable, can't add track after starting writing");
+ }
+ if (!initialized) {
+ if (defferedTracks == null) {
+ defferedTracks = new ArrayList<>();
+ }
+ defferedTracks.add(track);
+ return;
+ }
tracks.addTrack(track);
}
/**
- * Adds a tag to the file. You may add tags at any time before close()ing.
+ * Adds a tag to the file. You may add tags at any time before close()ing, only if DataWriter is seekable.
+ * Otherwise you may add tags only before frames.
*
* @param tag
*/
public void addTag(final MatroskaFileTagEntry tag)
{
+ if (initialized && !ioDW.isSeekable()) {
+ throw new UnsupportedOperationException("DataWriter isn't seekable, can't add tag after starting writing");
+ }
+ if (!initialized) {
+ if (defferedTags == null) {
+ defferedTags = new ArrayList<>();
+ }
+ defferedTags.add(tag);
+ return;
+ }
tags.addTag(tag);
}
@@ -180,6 +268,7 @@ public void addTag(final MatroskaFileTagEntry tag)
*/
public void silenceTrack(final long trackNumber)
{
+ initialize();
cluster.silenceTrack(trackNumber);
}
@@ -188,6 +277,7 @@ public void silenceTrack(final long trackNumber)
*/
public void unsilenceTrack(final long trackNumber)
{
+ initialize();
cluster.unsilenceTrack(trackNumber);
}
@@ -198,6 +288,7 @@ public void unsilenceTrack(final long trackNumber)
*/
public void addFrame(final MatroskaFileFrame frame)
{
+ initialize();
if (!cluster.addFrame(frame))
{
flush();
@@ -209,8 +300,13 @@ public void addFrame(final MatroskaFileFrame frame)
*/
public void flush()
{
- final long clusterPos = ioDW.getFilePointer();
- cueData.addCue(clusterPos, cluster.getClusterTimecode(), cluster.getTracks());
+ initialize();
+
+ if (cueData != null) {
+ final long clusterPos = ioDW.getFilePointer();
+ cueData.addCue(clusterPos, cluster.getClusterTimecode(), cluster.getTracks());
+ }
+
LOG.debug("Cluster flushing, timecode {}", cluster.getClusterTimecode());
cluster.flush(ioDW);
}
@@ -222,10 +318,12 @@ public void close()
{
flush();
- cueData.write(ioDW, metaSeek);
- metaSeek.update(ioDW);
- segmentInfoElem.update(ioDW);
- tracks.update(ioDW);
- tags.update(ioDW);
+ if (ioDW.isSeekable()) {
+ cueData.write(ioDW, metaSeek);
+ metaSeek.update(ioDW);
+ segmentInfoElem.update(ioDW);
+ tracks.update(ioDW);
+ tags.update(ioDW);
+ }
}
}
diff --git a/src/test/java/org/ebml/matroska/MatroskaFileWriterTest.java b/src/test/java/org/ebml/matroska/MatroskaFileWriterTest.java
index 2c90275..a181437 100644
--- a/src/test/java/org/ebml/matroska/MatroskaFileWriterTest.java
+++ b/src/test/java/org/ebml/matroska/MatroskaFileWriterTest.java
@@ -1,13 +1,5 @@
package org.ebml.matroska;
-import static org.junit.Assert.assertEquals;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.nio.charset.StandardCharsets;
-
import org.ebml.EBMLReader;
import org.ebml.Element;
import org.ebml.MasterElement;
@@ -22,6 +14,14 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+
+import static org.junit.Assert.assertEquals;
+
public class MatroskaFileWriterTest
{
private static final Logger LOG = LoggerFactory.getLogger(MatroskaFileWriterTest.class);
@@ -35,7 +35,7 @@ public class MatroskaFileWriterTest
public void setUp() throws Exception
{
destination = File.createTempFile("test", ".mkv");
- ioDW = new FileDataWriter(destination.getPath());
+ ioDW = createDataWriter(destination);
testTrack = new MatroskaFileTrack();
testTrack.setTrackNo(42);
testTrack.setTrackType(TrackType.SUBTITLE);
@@ -48,6 +48,10 @@ public void setUp() throws Exception
testTag.addSimpleTag(simpleTag);
}
+ protected FileDataWriter createDataWriter(File destination) throws Exception {
+ return new FileDataWriter(destination.getPath());
+ }
+
@After
public void tearDown() throws Exception
{
@@ -60,8 +64,8 @@ public void testWrite() throws FileNotFoundException, IOException
{
final MatroskaFileWriter writer = new MatroskaFileWriter(ioDW);
writer.addTrack(testTrack);
- writer.addFrame(generateFrame("I know a song...", 42));
writer.addTag(testTag);
+ writer.addFrame(generateFrame("I know a song...", 42));
writer.close();
final FileDataSource inputDataSource = new FileDataSource(destination.getPath());
@@ -78,26 +82,29 @@ public void testMultipleTracks() throws Exception
{
final MatroskaFileWriter writer = new MatroskaFileWriter(ioDW);
writer.addTrack(testTrack);
- writer.addFrame(generateFrame("I know a song...", 42));
+
final MatroskaFileTrack nextTrack = new MatroskaFileTrack();
nextTrack.setTrackNo(2);
nextTrack.setTrackType(TrackType.CONTROL);
nextTrack.setCodecID("some logo thingy");
nextTrack.setDefaultDuration(4242);
writer.addTrack(nextTrack);
- writer.addFrame(generateFrame("that gets on everybody's nerves", 2));
final MatroskaFileTrack virtualTrack = new MatroskaFileTrack();
virtualTrack.setTrackNo(3);
virtualTrack.setTrackType(TrackType.CONTROL);
virtualTrack.setCodecID("virtual tracky!");
virtualTrack.setDefaultDuration(1313);
+
final TrackOperation operation = new TrackOperation();
operation.addVirtualTrackPart(42);
operation.addVirtualTrackPart(2);
virtualTrack.setOperation(operation);
writer.addTrack(virtualTrack);
+ writer.addFrame(generateFrame("I know a song...", 42));
+ writer.addFrame(generateFrame("that gets on everybody's nerves", 2));
+
writer.close();
final FileDataSource inputDataSource = new FileDataSource(destination.getPath());
diff --git a/src/test/java/org/ebml/matroska/MatroskaUnseekableWriterTest.java b/src/test/java/org/ebml/matroska/MatroskaUnseekableWriterTest.java
new file mode 100644
index 0000000..114386d
--- /dev/null
+++ b/src/test/java/org/ebml/matroska/MatroskaUnseekableWriterTest.java
@@ -0,0 +1,24 @@
+package org.ebml.matroska;
+
+import org.ebml.io.FileDataWriter;
+import org.junit.Assert;
+
+import java.io.File;
+
+public class MatroskaUnseekableWriterTest extends MatroskaFileWriterTest {
+ @Override
+ protected FileDataWriter createDataWriter(File destination) throws Exception {
+ return new FileDataWriter(destination.getPath()){
+ @Override
+ public boolean isSeekable() {
+ return false;
+ }
+
+ @Override
+ public long seek(long pos) {
+ Assert.fail("Must not be called");
+ return -1;
+ }
+ };
+ }
+}
From 0977f1ab2525bffddbcfffd8049b1fc7139e9c03 Mon Sep 17 00:00:00 2001
From: Michael Jameson
Date: Mon, 10 Jul 2017 14:43:07 -0700
Subject: [PATCH 17/42] Cleanup of non-seekable writer
---
.../org/ebml/matroska/MatroskaFileTags.java | 21 ++-
.../org/ebml/matroska/MatroskaFileTracks.java | 10 +-
.../org/ebml/matroska/MatroskaFileWriter.java | 127 ++++++------------
.../ebml/matroska/MatroskaSegmentInfo.java | 8 +-
4 files changed, 55 insertions(+), 111 deletions(-)
diff --git a/src/main/java/org/ebml/matroska/MatroskaFileTags.java b/src/main/java/org/ebml/matroska/MatroskaFileTags.java
index 35e5f96..3fa7353 100644
--- a/src/main/java/org/ebml/matroska/MatroskaFileTags.java
+++ b/src/main/java/org/ebml/matroska/MatroskaFileTags.java
@@ -14,12 +14,7 @@ public class MatroskaFileTags
private final ArrayList tags = new ArrayList<>();
- private final long myPosition;
-
- public MatroskaFileTags(final long position)
- {
- myPosition = position;
- }
+ private long myPosition;
public void addTag(final MatroskaFileTagEntry tag)
{
@@ -28,16 +23,20 @@ public void addTag(final MatroskaFileTagEntry tag)
public long writeTags(final DataWriter ioDW)
{
+ myPosition = ioDW.getFilePointer();
final MasterElement tagsElem = MatroskaDocTypes.Tags.getInstance();
- for (final MatroskaFileTagEntry tag: tags)
+ for (final MatroskaFileTagEntry tag : tags)
{
tagsElem.addChildElement(tag.toElement());
}
- tagsElem.writeElement(ioDW);
- assert BLOCK_SIZE > tagsElem.getTotalSize();
- new VoidElement(BLOCK_SIZE - tagsElem.getTotalSize()).writeElement(ioDW);
- return BLOCK_SIZE;
+ long len = tagsElem.writeElement(ioDW);
+ if (ioDW.isSeekable())
+ {
+ new VoidElement(BLOCK_SIZE - tagsElem.getTotalSize()).writeElement(ioDW);
+ return BLOCK_SIZE;
+ }
+ return len;
}
public void update(final DataWriter ioDW)
diff --git a/src/main/java/org/ebml/matroska/MatroskaFileTracks.java b/src/main/java/org/ebml/matroska/MatroskaFileTracks.java
index 3fb936a..274c7bc 100644
--- a/src/main/java/org/ebml/matroska/MatroskaFileTracks.java
+++ b/src/main/java/org/ebml/matroska/MatroskaFileTracks.java
@@ -14,12 +14,7 @@ public class MatroskaFileTracks
private final ArrayList tracks = new ArrayList<>();
- private final long myPosition;
-
- public MatroskaFileTracks(final long position)
- {
- myPosition = position;
- }
+ private long myPosition;
public void addTrack(final MatroskaFileTrack track)
{
@@ -28,9 +23,10 @@ public void addTrack(final MatroskaFileTrack track)
public long writeTracks(final DataWriter ioDW)
{
+ myPosition = ioDW.getFilePointer();
final MasterElement tracksElem = MatroskaDocTypes.Tracks.getInstance();
- for (final MatroskaFileTrack track: tracks)
+ for (final MatroskaFileTrack track : tracks)
{
tracksElem.addChildElement(track.toElement());
}
diff --git a/src/main/java/org/ebml/matroska/MatroskaFileWriter.java b/src/main/java/org/ebml/matroska/MatroskaFileWriter.java
index 2f0acb3..ca83cf1 100644
--- a/src/main/java/org/ebml/matroska/MatroskaFileWriter.java
+++ b/src/main/java/org/ebml/matroska/MatroskaFileWriter.java
@@ -2,17 +2,17 @@
* JEBML - Java library to read/write EBML/Matroska elements.
* Copyright (C) 2004 Jory Stone
* Based on Javatroska (C) 2002 John Cannon
- *
+ *
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
- *
+ *
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
- *
+ *
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
@@ -26,9 +26,6 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import java.util.ArrayList;
-import java.util.List;
-
/**
* Primary API entrypoint for writing Matroska files.
*/
@@ -41,14 +38,10 @@ public class MatroskaFileWriter
private MatroskaFileMetaSeek metaSeek;
private MatroskaFileCues cueData;
private MatroskaCluster cluster;
- private MatroskaSegmentInfo segmentInfoElem;
- private MatroskaFileTracks tracks;
- private MatroskaFileTags tags;
-
- private Long defferedTimecodeScale;
- private Double defferedDuration;
- private List defferedTracks;
- private List defferedTags;
+ private MatroskaSegmentInfo segmentInfoElem = new MatroskaSegmentInfo();
+ private MatroskaFileTracks tracks = new MatroskaFileTracks();;
+ private MatroskaFileTags tags = new MatroskaFileTags();
+
private boolean initialized = false;
/**
@@ -57,63 +50,39 @@ public class MatroskaFileWriter
public MatroskaFileWriter(final DataWriter outputDataWriter)
{
ioDW = outputDataWriter;
- }
-
- void initialize() {
- if (initialized) {
- return;
- }
writeEBMLHeader();
writeSegmentHeader();
long endOfSegmentHeader = ioDW.getFilePointer();
- if (ioDW.isSeekable()) {
- metaSeek = new MatroskaFileMetaSeek(endOfSegmentHeader);
- cueData = new MatroskaFileCues(endOfSegmentHeader);
+ metaSeek = new MatroskaFileMetaSeek(endOfSegmentHeader);
+ cueData = new MatroskaFileCues(endOfSegmentHeader);
+ if (ioDW.isSeekable())
+ {
metaSeek.write(ioDW);
}
+ }
- segmentInfoElem = new MatroskaSegmentInfo(ioDW.getFilePointer());
- if (metaSeek != null) {
- metaSeek.addIndexedElement(MatroskaDocTypes.Info.getType(), ioDW.getFilePointer());
- }
- if (defferedTimecodeScale != null) {
- segmentInfoElem.setTimecodeScale(defferedTimecodeScale);
- }
- if (defferedDuration != null) {
- segmentInfoElem.setDuration(defferedDuration);
+ void initialize()
+ {
+ if (initialized)
+ {
+ return;
}
+
+ metaSeek.addIndexedElement(MatroskaDocTypes.Info.getType(), ioDW.getFilePointer());
segmentInfoElem.writeElement(ioDW);
- if (metaSeek != null) {
- metaSeek.addIndexedElement(MatroskaDocTypes.Tracks.getType(), ioDW.getFilePointer());
- }
- tracks = new MatroskaFileTracks(ioDW.getFilePointer());
- if (defferedTracks != null) {
- for (MatroskaFileTrack track : defferedTracks) {
- tracks.addTrack(track);
- }
- }
+ metaSeek.addIndexedElement(MatroskaDocTypes.Tracks.getType(), ioDW.getFilePointer());
tracks.writeTracks(ioDW);
- if (metaSeek != null) {
- metaSeek.addIndexedElement(MatroskaDocTypes.Tags.getType(), ioDW.getFilePointer());
- }
- tags = new MatroskaFileTags(ioDW.getFilePointer());
- if (defferedTags != null) {
- for (MatroskaFileTagEntry tag : defferedTags) {
- tags.addTag(tag);
- }
- }
+ metaSeek.addIndexedElement(MatroskaDocTypes.Tags.getType(), ioDW.getFilePointer());
tags.writeTags(ioDW);
cluster = new MatroskaCluster();
cluster.setLimitParameters(5000, 128 * 1024);
- if (metaSeek != null) {
- metaSeek.addIndexedElement(MatroskaDocTypes.Cluster.getType(), ioDW.getFilePointer());
- }
+ metaSeek.addIndexedElement(MatroskaDocTypes.Cluster.getType(), ioDW.getFilePointer());
initialized = true;
}
@@ -182,18 +151,15 @@ public long getTimecodeScale()
/**
* Sets the time scale used in this file. This is the number of nanoseconds represented by the timecode unit in frames. Defaults to 1,000,000.
- *
+ *
* @param timecodeScale
*/
public void setTimecodeScale(final long timecodeScale)
{
- if (initialized && !ioDW.isSeekable()) {
+ if (initialized && !ioDW.isSeekable())
+ {
throw new UnsupportedOperationException("DataWriter isn't seekable, can't change timecodeScale after starting writing");
}
- if (!initialized) {
- defferedTimecodeScale = timecodeScale;
- return;
- }
segmentInfoElem.setTimecodeScale(timecodeScale);
}
@@ -204,18 +170,15 @@ public double getDuration()
/**
* Sets the duration of the file. Optional.
- *
+ *
* @param duration
*/
public void setDuration(final double duration)
{
- if (initialized && !ioDW.isSeekable()) {
+ if (initialized && !ioDW.isSeekable())
+ {
throw new UnsupportedOperationException("DataWriter isn't seekable, can't change duration after starting writing");
}
- if (!initialized) {
- defferedDuration = duration;
- return;
- }
segmentInfoElem.setDuration(duration);
}
@@ -224,42 +187,30 @@ public void setDuration(final double duration)
*
* You may add tracks at any time before close()ing, only if DataWriter is seekable.
* Otherwise you may add tracks only before frames.
- *
+ *
* @param track
*/
public void addTrack(final MatroskaFileTrack track)
{
- if (initialized && !ioDW.isSeekable()) {
+ if (initialized && !ioDW.isSeekable())
+ {
throw new UnsupportedOperationException("DataWriter isn't seekable, can't add track after starting writing");
}
- if (!initialized) {
- if (defferedTracks == null) {
- defferedTracks = new ArrayList<>();
- }
- defferedTracks.add(track);
- return;
- }
tracks.addTrack(track);
}
/**
* Adds a tag to the file. You may add tags at any time before close()ing, only if DataWriter is seekable.
* Otherwise you may add tags only before frames.
- *
+ *
* @param tag
*/
public void addTag(final MatroskaFileTagEntry tag)
{
- if (initialized && !ioDW.isSeekable()) {
+ if (initialized && !ioDW.isSeekable())
+ {
throw new UnsupportedOperationException("DataWriter isn't seekable, can't add tag after starting writing");
}
- if (!initialized) {
- if (defferedTags == null) {
- defferedTags = new ArrayList<>();
- }
- defferedTags.add(tag);
- return;
- }
tags.addTag(tag);
}
@@ -283,7 +234,7 @@ public void unsilenceTrack(final long trackNumber)
/**
* Add a frame
- *
+ *
* @param frame The frame to add
*/
public void addFrame(final MatroskaFileFrame frame)
@@ -296,13 +247,14 @@ public void addFrame(final MatroskaFileFrame frame)
}
/**
- * Flushes pending content to disk and starts a new cluster. This is typically not necessary to call manually.
+ * Flushes pending content to disk and starts a new cluster. This is typically not necessary to call manually.
*/
public void flush()
{
initialize();
- if (cueData != null) {
+ if (ioDW.isSeekable())
+ {
final long clusterPos = ioDW.getFilePointer();
cueData.addCue(clusterPos, cluster.getClusterTimecode(), cluster.getTracks());
}
@@ -318,7 +270,8 @@ public void close()
{
flush();
- if (ioDW.isSeekable()) {
+ if (ioDW.isSeekable())
+ {
cueData.write(ioDW, metaSeek);
metaSeek.update(ioDW);
segmentInfoElem.update(ioDW);
diff --git a/src/main/java/org/ebml/matroska/MatroskaSegmentInfo.java b/src/main/java/org/ebml/matroska/MatroskaSegmentInfo.java
index 6725686..0470354 100644
--- a/src/main/java/org/ebml/matroska/MatroskaSegmentInfo.java
+++ b/src/main/java/org/ebml/matroska/MatroskaSegmentInfo.java
@@ -19,15 +19,11 @@ public class MatroskaSegmentInfo
private long timecodeScale = 1000000;
private Double duration;
private Date segmentDate = new Date();
- private final long myPosition;
-
- public MatroskaSegmentInfo(final long position)
- {
- myPosition = position;
- }
+ private long myPosition;
public long writeElement(final DataWriter ioDW)
{
+ myPosition = ioDW.getFilePointer();
final MasterElement segmentInfoElem = MatroskaDocTypes.Info.getInstance();
final StringElement writingAppElem = MatroskaDocTypes.WritingApp.getInstance();
From bdf16b6d63b37b11751fcf590621c4043cb9e095 Mon Sep 17 00:00:00 2001
From: Michael Jameson
Date: Mon, 10 Jul 2017 14:43:34 -0700
Subject: [PATCH 18/42] Suppress unused warnings
---
src/test/java/org/ebml/MockSource.java | 2 +-
src/test/java/org/ebml/MockWriter.java | 1 +
2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/src/test/java/org/ebml/MockSource.java b/src/test/java/org/ebml/MockSource.java
index 135e27b..fe48e24 100644
--- a/src/test/java/org/ebml/MockSource.java
+++ b/src/test/java/org/ebml/MockSource.java
@@ -8,7 +8,7 @@
public class MockSource implements DataSource
{
-
+ @SuppressWarnings("unused")
private static final Logger LOG = LoggerFactory.getLogger(MockSource.class);
private final ByteBuffer buffer;
diff --git a/src/test/java/org/ebml/MockWriter.java b/src/test/java/org/ebml/MockWriter.java
index 0783fac..ddc2bd8 100644
--- a/src/test/java/org/ebml/MockWriter.java
+++ b/src/test/java/org/ebml/MockWriter.java
@@ -8,6 +8,7 @@
public class MockWriter implements DataWriter
{
+ @SuppressWarnings("unused")
private static final Logger LOG = LoggerFactory.getLogger(MockWriter.class);
private final ByteBuffer buff = ByteBuffer.allocate(4096);
From dccbc8f26e59437a2ca0b5dfe81ebc86a190ae83 Mon Sep 17 00:00:00 2001
From: Michael Jameson
Date: Mon, 10 Jul 2017 15:01:41 -0700
Subject: [PATCH 19/42] Updating cue creation based on updated Matroska spec
---
build.gradle | 2 +-
.../org/ebml/matroska/MatroskaCluster.java | 30 +++++++--------
.../org/ebml/matroska/MatroskaFileCues.java | 38 +++++++------------
.../org/ebml/matroska/MatroskaFileWriter.java | 29 +++++++++-----
4 files changed, 49 insertions(+), 50 deletions(-)
diff --git a/build.gradle b/build.gradle
index eb24e74..7f397fb 100644
--- a/build.gradle
+++ b/build.gradle
@@ -68,7 +68,7 @@ publishing {
}
}
-project.version = '2.2'
+project.version = '2.3.0'
bundle {
instructions << [
diff --git a/src/main/java/org/ebml/matroska/MatroskaCluster.java b/src/main/java/org/ebml/matroska/MatroskaCluster.java
index 20efc83..82fe369 100644
--- a/src/main/java/org/ebml/matroska/MatroskaCluster.java
+++ b/src/main/java/org/ebml/matroska/MatroskaCluster.java
@@ -2,17 +2,17 @@
* JEBML - Java library to read/write EBML/Matroska elements.
* Copyright (C) 2004 Jory Stone
* Based on Javatroska (C) 2002 John Cannon
- *
+ *
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
- *
+ *
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
- *
+ *
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
@@ -44,27 +44,25 @@ class MatroskaCluster
private final List sliencedTracks = new ArrayList<>();
private long clusterTimecode = Long.MAX_VALUE;
- private int sizeLimit = Integer.MAX_VALUE;
- private int totalSize = 0;
- private long durationLimit = Long.MAX_VALUE;
+ private long lastTimecode = 0;
+ private long durationLimit = 500;
public MatroskaCluster()
{
}
- void setLimitParameters(final long duration, final int size)
+ void setLimitParameters(final long duration)
{
- this.sizeLimit = size;
this.durationLimit = duration;
}
/**
* Add a frame to the cluster
- *
+ *
* @param frame
* @return false if you should begin another cluster.
*/
- public boolean addFrame(final MatroskaFileFrame frame)
+ public void addFrame(final MatroskaFileFrame frame)
{
// Is this the earliest timecode?
if (frame.getTimecode() < clusterTimecode)
@@ -72,9 +70,12 @@ public boolean addFrame(final MatroskaFileFrame frame)
clusterTimecode = frame.getTimecode();
}
frames.add(frame);
- totalSize += frame.getData().remaining();
tracks.add(frame.getTrackNo());
- return ((frame.getTimecode() - clusterTimecode) < durationLimit) && (totalSize < sizeLimit);
+ }
+
+ public boolean isFlushNeeded()
+ {
+ return (lastTimecode - clusterTimecode) < durationLimit;
}
public long flush(final DataWriter ioDW)
@@ -93,7 +94,7 @@ public long flush(final DataWriter ioDW)
if (!sliencedTracks.isEmpty())
{
final MasterElement silentElem = MatroskaDocTypes.SilentTracks.getInstance();
- for (final Long silent: sliencedTracks)
+ for (final Long silent : sliencedTracks)
{
final UnsignedIntegerElement silentTrackElem = MatroskaDocTypes.SilentTrackNumber.getInstance();
silentTrackElem.setValue(silent);
@@ -107,7 +108,7 @@ public long flush(final DataWriter ioDW)
long lastTimecode = 0;
int lastTrackNumber = 0;
LOG.trace("Timecode for cluster set to {}", clusterTimecode);
- for (final MatroskaFileFrame frame: frames)
+ for (final MatroskaFileFrame frame : frames)
{
frame.setTimecode(frame.getTimecode() - clusterTimecode);
LOG.trace("Timecode for frame set to {}", frame.getTimecode());
@@ -133,7 +134,6 @@ public long flush(final DataWriter ioDW)
{
frames.clear();
tracks.clear();
- totalSize = 0;
clusterTimecode = Long.MAX_VALUE;
}
}
diff --git a/src/main/java/org/ebml/matroska/MatroskaFileCues.java b/src/main/java/org/ebml/matroska/MatroskaFileCues.java
index c06d547..e145ef2 100644
--- a/src/main/java/org/ebml/matroska/MatroskaFileCues.java
+++ b/src/main/java/org/ebml/matroska/MatroskaFileCues.java
@@ -1,7 +1,5 @@
package org.ebml.matroska;
-import java.util.Collection;
-
import org.ebml.Element;
import org.ebml.MasterElement;
import org.ebml.UnsignedIntegerElement;
@@ -20,32 +18,22 @@ public MatroskaFileCues(long endOfEbmlHeaderBytePosition)
this.endOfEbmlHeaderBytePosition = endOfEbmlHeaderBytePosition;
}
- public void addCue(long positionInFile, long timecodeOfCluster, Collection clusterTrackNumbers)
+ public void addCue(long positionInFile, long timecodeOfCluster, int trackNumber)
{
- if (!clusterTrackNumbers.isEmpty())
- {
- LOG.debug("Adding matroska cue to cues element at position [{}], using timecode [{}], for track numbers [{}]",
- positionInFile,
- timecodeOfCluster,
- clusterTrackNumbers);
+ LOG.debug("Adding matroska cue to cues element at position [{}], using timecode [{}], for track number [{}]",
+ positionInFile,
+ timecodeOfCluster,
+ trackNumber);
- UnsignedIntegerElement cueTime = MatroskaDocTypes.CueTime.getInstance();
- cueTime.setValue(timecodeOfCluster);
- MasterElement cuePoint = MatroskaDocTypes.CuePoint.getInstance();
- cuePoint.addChildElement(cueTime);
- for (int trackNumber : clusterTrackNumbers)
- {
- MasterElement cueTrackPositions = createCueTrackPositions(positionInFile, trackNumber);
- cuePoint.addChildElement(cueTrackPositions);
- }
- cues.addChildElement(cuePoint);
+ UnsignedIntegerElement cueTime = MatroskaDocTypes.CueTime.getInstance();
+ cueTime.setValue(timecodeOfCluster);
+ MasterElement cuePoint = MatroskaDocTypes.CuePoint.getInstance();
+ cuePoint.addChildElement(cueTime);
+ MasterElement cueTrackPositions = createCueTrackPositions(positionInFile, trackNumber);
+ cuePoint.addChildElement(cueTrackPositions);
+ cues.addChildElement(cuePoint);
- LOG.debug("Finished adding matroska cue to cues element");
- }
- else
- {
- LOG.debug("No track numbers specified. Not adding Cue.");
- }
+ LOG.debug("Finished adding matroska cue to cues element");
}
private MasterElement createCueTrackPositions(long positionInFile, int trackNumber)
diff --git a/src/main/java/org/ebml/matroska/MatroskaFileWriter.java b/src/main/java/org/ebml/matroska/MatroskaFileWriter.java
index ca83cf1..7fd45aa 100644
--- a/src/main/java/org/ebml/matroska/MatroskaFileWriter.java
+++ b/src/main/java/org/ebml/matroska/MatroskaFileWriter.java
@@ -19,6 +19,9 @@
*/
package org.ebml.matroska;
+import java.util.HashSet;
+import java.util.Set;
+
import org.ebml.MasterElement;
import org.ebml.StringElement;
import org.ebml.UnsignedIntegerElement;
@@ -41,9 +44,11 @@ public class MatroskaFileWriter
private MatroskaSegmentInfo segmentInfoElem = new MatroskaSegmentInfo();
private MatroskaFileTracks tracks = new MatroskaFileTracks();;
private MatroskaFileTags tags = new MatroskaFileTags();
-
+ private Set videoTrackNumbers = new HashSet<>();
private boolean initialized = false;
+ private boolean onlyAudioTracks;
+
/**
* @param outputDataWriter DataWriter to write out to.
*/
@@ -81,10 +86,10 @@ void initialize()
tags.writeTags(ioDW);
cluster = new MatroskaCluster();
- cluster.setLimitParameters(5000, 128 * 1024);
metaSeek.addIndexedElement(MatroskaDocTypes.Cluster.getType(), ioDW.getFilePointer());
initialized = true;
+ onlyAudioTracks = videoTrackNumbers.isEmpty();
}
void writeEBMLHeader()
@@ -197,6 +202,10 @@ public void addTrack(final MatroskaFileTrack track)
throw new UnsupportedOperationException("DataWriter isn't seekable, can't add track after starting writing");
}
tracks.addTrack(track);
+ if (track.getVideo() != null)
+ {
+ videoTrackNumbers.add(track.getTrackNo());
+ }
}
/**
@@ -240,10 +249,18 @@ public void unsilenceTrack(final long trackNumber)
public void addFrame(final MatroskaFileFrame frame)
{
initialize();
- if (!cluster.addFrame(frame))
+ if ((onlyAudioTracks && cluster.isFlushNeeded())
+ || (frame.isKeyFrame() && videoTrackNumbers.contains(frame.getTrackNo())))
{
flush();
+
+ if (ioDW.isSeekable())
+ {
+ final long clusterPos = ioDW.getFilePointer();
+ cueData.addCue(clusterPos, frame.getTimecode(), frame.getTrackNo());
+ }
}
+ cluster.addFrame(frame);
}
/**
@@ -253,12 +270,6 @@ public void flush()
{
initialize();
- if (ioDW.isSeekable())
- {
- final long clusterPos = ioDW.getFilePointer();
- cueData.addCue(clusterPos, cluster.getClusterTimecode(), cluster.getTracks());
- }
-
LOG.debug("Cluster flushing, timecode {}", cluster.getClusterTimecode());
cluster.flush(ioDW);
}
From 5bf67e3551498701404d5349055ef905d671ad84 Mon Sep 17 00:00:00 2001
From: Michael Jameson
Date: Mon, 10 Jul 2017 15:07:50 -0700
Subject: [PATCH 20/42] Checkstyle changes
---
.../org/ebml/matroska/MatroskaCluster.java | 7 ++--
.../org/ebml/matroska/MatroskaFileTrack.java | 38 +++++++++++-------
.../ebml/matroska/MatroskaFileWriterTest.java | 19 ++++-----
.../MatroskaUnseekableWriterTest.java | 39 +++++++++++--------
4 files changed, 60 insertions(+), 43 deletions(-)
diff --git a/src/main/java/org/ebml/matroska/MatroskaCluster.java b/src/main/java/org/ebml/matroska/MatroskaCluster.java
index 82fe369..01e3054 100644
--- a/src/main/java/org/ebml/matroska/MatroskaCluster.java
+++ b/src/main/java/org/ebml/matroska/MatroskaCluster.java
@@ -69,6 +69,7 @@ public void addFrame(final MatroskaFileFrame frame)
{
clusterTimecode = frame.getTimecode();
}
+ lastTimecode = frame.getTimecode();
frames.add(frame);
tracks.add(frame.getTrackNo());
}
@@ -105,14 +106,14 @@ public long flush(final DataWriter ioDW)
MatroskaSimpleBlock block = null;
boolean forceNew = true;
- long lastTimecode = 0;
+ long prevTimecode = 0;
int lastTrackNumber = 0;
LOG.trace("Timecode for cluster set to {}", clusterTimecode);
for (final MatroskaFileFrame frame : frames)
{
frame.setTimecode(frame.getTimecode() - clusterTimecode);
LOG.trace("Timecode for frame set to {}", frame.getTimecode());
- if (forceNew || lastTimecode != frame.getTimecode() || lastTrackNumber != frame.getTrackNo())
+ if (forceNew || prevTimecode != frame.getTimecode() || lastTrackNumber != frame.getTrackNo())
{
if (block != null)
{
@@ -120,7 +121,7 @@ public long flush(final DataWriter ioDW)
}
block = new MatroskaSimpleBlock();
}
- lastTimecode = frame.getTimecode();
+ prevTimecode = frame.getTimecode();
lastTrackNumber = frame.getTrackNo();
forceNew = !block.addFrame(frame);
}
diff --git a/src/main/java/org/ebml/matroska/MatroskaFileTrack.java b/src/main/java/org/ebml/matroska/MatroskaFileTrack.java
index 1d6d4cf..86c805f 100644
--- a/src/main/java/org/ebml/matroska/MatroskaFileTrack.java
+++ b/src/main/java/org/ebml/matroska/MatroskaFileTrack.java
@@ -2,31 +2,37 @@
* JEBML - Java library to read/write EBML/Matroska elements.
* Copyright (C) 2004 Jory Stone
* Based on Javatroska (C) 2002 John Cannon
- *
+ *
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
- *
+ *
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
- *
+ *
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package org.ebml.matroska;
-import org.ebml.*;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+
+import org.ebml.BinaryElement;
+import org.ebml.EBMLReader;
+import org.ebml.Element;
+import org.ebml.FloatElement;
+import org.ebml.MasterElement;
+import org.ebml.StringElement;
+import org.ebml.UnsignedIntegerElement;
import org.ebml.io.DataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import java.nio.ByteBuffer;
-import java.util.ArrayList;
-
/**
* Matroska Track Class
* Mandatory fields are:
@@ -150,11 +156,13 @@ public void setDisplayHeight(final short displayHeight)
this.displayHeight = displayHeight;
}
- public ByteBuffer getColourSpace() {
+ public ByteBuffer getColourSpace()
+ {
return colourSpace;
}
- public void setColourSpace(ByteBuffer colourSpace) {
+ public void setColourSpace(ByteBuffer colourSpace)
+ {
this.colourSpace = colourSpace;
}
}
@@ -228,7 +236,7 @@ public void addVirtualTrackPart(final long uid)
/**
* Converts the Track to String form
- *
+ *
* @return String form of MatroskaFileTrack data
*/
@Override
@@ -488,7 +496,7 @@ Element toElement()
if (!overlayUids.isEmpty())
{
- for (final Long overlay: overlayUids)
+ for (final Long overlay : overlayUids)
{
final UnsignedIntegerElement trackOverlayElem = MatroskaDocTypes.TrackOverlay.getInstance();
trackOverlayElem.setValue(overlay);
@@ -514,7 +522,8 @@ Element toElement()
trackVideoDisplayHeightElem.setValue(this.video.getDisplayHeight());
BinaryElement colourSpaceElem = null;
- if (this.video.getColourSpace() != null) {
+ if (this.video.getColourSpace() != null)
+ {
colourSpaceElem = MatroskaDocTypes.ColourSpace.getInstance();
colourSpaceElem.setData(this.video.getColourSpace());
}
@@ -523,7 +532,8 @@ Element toElement()
trackVideoElem.addChildElement(trackVideoPixelHeightElem);
trackVideoElem.addChildElement(trackVideoDisplayWidthElem);
trackVideoElem.addChildElement(trackVideoDisplayHeightElem);
- if (colourSpaceElem != null) {
+ if (colourSpaceElem != null)
+ {
trackVideoElem.addChildElement(colourSpaceElem);
}
@@ -556,7 +566,7 @@ else if (this.getTrackType() == TrackType.AUDIO)
{
final MasterElement trackOpElem = MatroskaDocTypes.TrackOperation.getInstance();
final MasterElement trackJoinElem = MatroskaDocTypes.TrackJoinBlocks.getInstance();
- for (final Long uid: operation.joinUIDs)
+ for (final Long uid : operation.joinUIDs)
{
final UnsignedIntegerElement joinUidElem = MatroskaDocTypes.TrackJoinUID.getInstance();
joinUidElem.setValue(uid);
diff --git a/src/test/java/org/ebml/matroska/MatroskaFileWriterTest.java b/src/test/java/org/ebml/matroska/MatroskaFileWriterTest.java
index a181437..2fe1787 100644
--- a/src/test/java/org/ebml/matroska/MatroskaFileWriterTest.java
+++ b/src/test/java/org/ebml/matroska/MatroskaFileWriterTest.java
@@ -1,5 +1,13 @@
package org.ebml.matroska;
+import static org.junit.Assert.assertEquals;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+
import org.ebml.EBMLReader;
import org.ebml.Element;
import org.ebml.MasterElement;
@@ -14,14 +22,6 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.nio.charset.StandardCharsets;
-
-import static org.junit.Assert.assertEquals;
-
public class MatroskaFileWriterTest
{
private static final Logger LOG = LoggerFactory.getLogger(MatroskaFileWriterTest.class);
@@ -48,7 +48,8 @@ public void setUp() throws Exception
testTag.addSimpleTag(simpleTag);
}
- protected FileDataWriter createDataWriter(File destination) throws Exception {
+ protected FileDataWriter createDataWriter(File destination) throws Exception
+ {
return new FileDataWriter(destination.getPath());
}
diff --git a/src/test/java/org/ebml/matroska/MatroskaUnseekableWriterTest.java b/src/test/java/org/ebml/matroska/MatroskaUnseekableWriterTest.java
index 114386d..28e7f4b 100644
--- a/src/test/java/org/ebml/matroska/MatroskaUnseekableWriterTest.java
+++ b/src/test/java/org/ebml/matroska/MatroskaUnseekableWriterTest.java
@@ -1,24 +1,29 @@
package org.ebml.matroska;
+import java.io.File;
+
import org.ebml.io.FileDataWriter;
import org.junit.Assert;
-import java.io.File;
-
-public class MatroskaUnseekableWriterTest extends MatroskaFileWriterTest {
- @Override
- protected FileDataWriter createDataWriter(File destination) throws Exception {
- return new FileDataWriter(destination.getPath()){
- @Override
- public boolean isSeekable() {
- return false;
- }
+public class MatroskaUnseekableWriterTest extends MatroskaFileWriterTest
+{
+ @Override
+ protected FileDataWriter createDataWriter(File destination) throws Exception
+ {
+ return new FileDataWriter(destination.getPath())
+ {
+ @Override
+ public boolean isSeekable()
+ {
+ return false;
+ }
- @Override
- public long seek(long pos) {
- Assert.fail("Must not be called");
- return -1;
- }
- };
- }
+ @Override
+ public long seek(long pos)
+ {
+ Assert.fail("Must not be called");
+ return -1;
+ }
+ };
+ }
}
From 08d256c86dabd96d557ddf3c256ecc912b4c09b4 Mon Sep 17 00:00:00 2001
From: Denis Kokorin
Date: Tue, 15 Aug 2017 19:10:54 +0300
Subject: [PATCH 21/42] Add lazy reading of frames (#6)
---
src/main/java/org/ebml/matroska/MatroskaFile.java | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/src/main/java/org/ebml/matroska/MatroskaFile.java b/src/main/java/org/ebml/matroska/MatroskaFile.java
index 27af431..322923f 100644
--- a/src/main/java/org/ebml/matroska/MatroskaFile.java
+++ b/src/main/java/org/ebml/matroska/MatroskaFile.java
@@ -54,6 +54,8 @@ public class MatroskaFile
private final DataSource ioDS;
private final EBMLReader reader;
private Element level0 = null;
+ // Level1 is required fo lazy reading of frames
+ private Element level1 = null;
private String segmentTitle;
private Date segmentDate;
private String muxingApp;
@@ -315,8 +317,12 @@ private void fillFrameQueue()
synchronized (level0)
{
- Element level1 = ((MasterElement) level0).readNextChild(reader);
- while (level1 != null)
+ if (level1 == null)
+ {
+ level1 = ((MasterElement) level0).readNextChild(reader);
+ }
+
+ while (frameQueue.isEmpty() && level1 != null)
{
if (level1.isType(MatroskaDocTypes.Cluster.getType()))
{
From da23a55e0d4076d9b7bf46d00076f9a8bdd97378 Mon Sep 17 00:00:00 2001
From: Michael Jameson
Date: Wed, 23 Oct 2019 09:12:30 -0700
Subject: [PATCH 22/42] Bump version, add sources and javadoc jars
---
build.gradle | 18 +++++++++++++++++-
1 file changed, 17 insertions(+), 1 deletion(-)
diff --git a/build.gradle b/build.gradle
index 7f397fb..69f8864 100644
--- a/build.gradle
+++ b/build.gradle
@@ -58,17 +58,33 @@ sourceSets {
}
}
+task sourcesJar(type: Jar, dependsOn: classes) {
+ from sourceSets.main.allSource
+}
+javadoc {
+ failOnError = false
+}
+task javadocJar(type: Jar, dependsOn: javadoc) {
+ from javadoc.destinationDir
+}
+
publishing {
publications {
"${project.name}Jar"(MavenPublication) {
from components.java
version project.version.toString()
artifactId project.name
+ artifact sourcesJar {
+ classifier "sources"
+ }
+ artifact javadocJar {
+ classifier "javadoc"
+ }
}
}
}
-project.version = '2.3.0'
+project.version = '2.3.1'
bundle {
instructions << [
From 3106cc9cbb0d4f3d52b87ffbeb253d62148e349b Mon Sep 17 00:00:00 2001
From: Michael Jameson
Date: Wed, 23 Oct 2019 09:18:57 -0700
Subject: [PATCH 23/42] Update to latest gradle
---
build.gradle | 21 +++++++--------------
tools/gradle-wrapper.properties | 2 +-
2 files changed, 8 insertions(+), 15 deletions(-)
diff --git a/build.gradle b/build.gradle
index 69f8864..4e10a4c 100644
--- a/build.gradle
+++ b/build.gradle
@@ -2,7 +2,7 @@ apply plugin: 'java'
apply plugin: 'maven'
apply plugin: 'findbugs'
apply plugin: 'checkstyle'
-apply plugin: 'org.dm.bundle'
+apply plugin: 'osgi'
apply plugin: 'maven-publish'
repositories {
@@ -10,15 +10,6 @@ repositories {
mavenCentral()
}
-buildscript {
- repositories {
- jcenter()
- }
- dependencies {
- classpath 'org.dm.gradle:gradle-bundle-plugin:0.6.2'
- }
-}
-
dependencies {
compile 'org.slf4j:slf4j-api:1.7.5'
testCompile 'junit:junit:4.11'
@@ -54,7 +45,7 @@ findbugsTest {
sourceSets {
main {
resources.srcDirs = java.srcDirs
- output.resourcesDir = output.classesDir
+ output.resourcesDir = output.classesDirs.first()
}
}
@@ -86,8 +77,9 @@ publishing {
project.version = '2.3.1'
-bundle {
- instructions << [
+jar {
+ manifest {
+ instructions << [
'Bundle-Name' : 'JEBML',
'Bundle-Version': version,
'Bundle-License': 'LGPLv2',
@@ -96,5 +88,6 @@ bundle {
'-nodefaultversion': true,
'-noee': true,
'majorMinor' : 1
- ]
+ ]
+ }
}
diff --git a/tools/gradle-wrapper.properties b/tools/gradle-wrapper.properties
index d45dada..c84d56b 100644
--- a/tools/gradle-wrapper.properties
+++ b/tools/gradle-wrapper.properties
@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.3-bin.zip
From 08fd130e1303f352394e661134d3900d946f304f Mon Sep 17 00:00:00 2001
From: Michael Jameson
Date: Wed, 23 Oct 2019 09:33:33 -0700
Subject: [PATCH 24/42] Add ability to get tags list
---
build.gradle | 3 +--
src/main/java/org/ebml/matroska/MatroskaFile.java | 6 ++++++
2 files changed, 7 insertions(+), 2 deletions(-)
diff --git a/build.gradle b/build.gradle
index 4e10a4c..da7e362 100644
--- a/build.gradle
+++ b/build.gradle
@@ -4,6 +4,7 @@ apply plugin: 'findbugs'
apply plugin: 'checkstyle'
apply plugin: 'osgi'
apply plugin: 'maven-publish'
+project.version = '2.3.2'
repositories {
mavenLocal()
@@ -63,7 +64,6 @@ publishing {
publications {
"${project.name}Jar"(MavenPublication) {
from components.java
- version project.version.toString()
artifactId project.name
artifact sourcesJar {
classifier "sources"
@@ -75,7 +75,6 @@ publishing {
}
}
-project.version = '2.3.1'
jar {
manifest {
diff --git a/src/main/java/org/ebml/matroska/MatroskaFile.java b/src/main/java/org/ebml/matroska/MatroskaFile.java
index 322923f..4f323c1 100644
--- a/src/main/java/org/ebml/matroska/MatroskaFile.java
+++ b/src/main/java/org/ebml/matroska/MatroskaFile.java
@@ -22,6 +22,7 @@
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
+import java.util.List;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
@@ -745,4 +746,9 @@ public boolean getScanFirstCluster()
{
return scanFirstCluster;
}
+
+ public List getTagList()
+ {
+ return tagList;
+ }
}
From 9d9141ab49900091b6678d6b0d1d2e92bc2052b7 Mon Sep 17 00:00:00 2001
From: Michael Jameson
Date: Wed, 23 Oct 2019 09:45:01 -0700
Subject: [PATCH 25/42] Add publishing repository that uses global properties
---
build.gradle | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/build.gradle b/build.gradle
index da7e362..ab9e36b 100644
--- a/build.gradle
+++ b/build.gradle
@@ -73,6 +73,17 @@ publishing {
}
}
}
+ if (project.hasProperty("maven_url")) {
+ repositories {
+ maven {
+ url maven_url
+ credentials(PasswordCredentials) {
+ username = maven_user
+ password = maven_password
+ }
+ }
+ }
+ }
}
From 9a5cb864f36253f23368377e50f253051445b710 Mon Sep 17 00:00:00 2001
From: Michael Jameson
Date: Fri, 25 Oct 2019 09:59:21 -0700
Subject: [PATCH 26/42] Expose tags within a tag entry
---
build.gradle | 2 +-
src/main/java/org/ebml/matroska/MatroskaFileTagEntry.java | 8 ++++----
2 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/build.gradle b/build.gradle
index ab9e36b..9ffef7f 100644
--- a/build.gradle
+++ b/build.gradle
@@ -4,7 +4,7 @@ apply plugin: 'findbugs'
apply plugin: 'checkstyle'
apply plugin: 'osgi'
apply plugin: 'maven-publish'
-project.version = '2.3.2'
+project.version = '2.3.3'
repositories {
mavenLocal()
diff --git a/src/main/java/org/ebml/matroska/MatroskaFileTagEntry.java b/src/main/java/org/ebml/matroska/MatroskaFileTagEntry.java
index c2f9644..dae48b7 100644
--- a/src/main/java/org/ebml/matroska/MatroskaFileTagEntry.java
+++ b/src/main/java/org/ebml/matroska/MatroskaFileTagEntry.java
@@ -2,17 +2,17 @@
* JEBML - Java library to read/write EBML/Matroska elements.
* Copyright (C) 2004 Jory Stone
* Based on Javatroska (C) 2002 John Cannon
- *
+ *
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
- *
+ *
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
- *
+ *
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
@@ -33,7 +33,7 @@ public class MatroskaFileTagEntry
public ArrayList trackUID = new ArrayList<>();
public ArrayList chapterUID = new ArrayList<>();
public ArrayList attachmentUID = new ArrayList<>();
- private List simpleTags = new ArrayList<>();
+ public List simpleTags = new ArrayList<>();
public void addSimpleTag(final MatroskaFileSimpleTag simpleTag)
{
From 3682b5a235d4427d1f505635031de0ede1506207 Mon Sep 17 00:00:00 2001
From: Michael Jameson
Date: Fri, 25 Oct 2019 10:21:50 -0700
Subject: [PATCH 27/42] Fix Java 12 deprecation warnings
---
src/main/java/org/ebml/ProtoType.java | 4 ++--
src/main/java/org/ebml/matroska/MatroskaFile.java | 14 +++++++-------
2 files changed, 9 insertions(+), 9 deletions(-)
diff --git a/src/main/java/org/ebml/ProtoType.java b/src/main/java/org/ebml/ProtoType.java
index a28e539..9a81886 100644
--- a/src/main/java/org/ebml/ProtoType.java
+++ b/src/main/java/org/ebml/ProtoType.java
@@ -32,12 +32,12 @@ public T getInstance()
LOG.trace("Instantiating {}", name);
try
{
- final T elem = clazz.newInstance();
+ final T elem = clazz.getConstructor().newInstance();
elem.setType(type);
elem.setElementType(this);
return elem;
}
- catch (InstantiationException | IllegalAccessException e)
+ catch (Exception e)
{
LOG.error("Failed to instantiate: this should never happen!", e);
throw new RuntimeException(e);
diff --git a/src/main/java/org/ebml/matroska/MatroskaFile.java b/src/main/java/org/ebml/matroska/MatroskaFile.java
index 4f323c1..fd8104c 100644
--- a/src/main/java/org/ebml/matroska/MatroskaFile.java
+++ b/src/main/java/org/ebml/matroska/MatroskaFile.java
@@ -2,17 +2,17 @@
* JEBML - Java library to read/write EBML/Matroska elements.
* Copyright (C) 2004 Jory Stone
* Based on Javatroska (C) 2002 John Cannon
- *
+ *
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
- *
+ *
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
- *
+ *
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
@@ -81,7 +81,7 @@ public MatroskaFile(final DataSource inputDataSource)
/**
* Read / Parse the Matroska file. Call this before any other method.
- *
+ *
* @throws RuntimeException On various errors
*/
public void readFile()
@@ -549,17 +549,17 @@ private void parseTags(final Element level1, Element level2)
if (level4.isType(MatroskaDocTypes.TagTrackUID.getType()))
{
level4.readData(ioDS);
- tag.trackUID.add(new Long(((UnsignedIntegerElement) level4).getValue()));
+ tag.trackUID.add(((UnsignedIntegerElement) level4).getValue());
}
else if (level4.isType(MatroskaDocTypes.TagChapterUID.getType()))
{
level4.readData(ioDS);
- tag.chapterUID.add(new Long(((UnsignedIntegerElement) level4).getValue()));
+ tag.chapterUID.add(((UnsignedIntegerElement) level4).getValue());
}
else if (level4.isType(MatroskaDocTypes.TagAttachmentUID.getType()))
{
level4.readData(ioDS);
- tag.attachmentUID.add(new Long(((UnsignedIntegerElement) level4).getValue()));
+ tag.attachmentUID.add(((UnsignedIntegerElement) level4).getValue());
}
level4.skipData(ioDS);
From 05809794e6df5b4a7cdeebecb944f635d41db7ed Mon Sep 17 00:00:00 2001
From: Michael Jameson
Date: Fri, 25 Oct 2019 10:25:35 -0700
Subject: [PATCH 28/42] Refactor MatroskaFile synchronization
---
.../java/org/ebml/matroska/MatroskaFile.java | 39 +++++++------------
1 file changed, 15 insertions(+), 24 deletions(-)
diff --git a/src/main/java/org/ebml/matroska/MatroskaFile.java b/src/main/java/org/ebml/matroska/MatroskaFile.java
index fd8104c..5d3122d 100644
--- a/src/main/java/org/ebml/matroska/MatroskaFile.java
+++ b/src/main/java/org/ebml/matroska/MatroskaFile.java
@@ -167,7 +167,7 @@ else if (level1.isType(MatroskaDocTypes.Tags.getType()))
*
* @return The next MatroskaFileFrame in the queue, or null if the file has ended
*/
- public MatroskaFileFrame getNextFrame()
+ public synchronized MatroskaFileFrame getNextFrame()
{
if (frameQueue.isEmpty())
{
@@ -188,7 +188,7 @@ public MatroskaFileFrame getNextFrame()
* @param trackNo The track number to only get MatroskaFileFrame(s) from
* @return The next MatroskaFileFrame in the queue, or null if there are no more frames for the TrackNo track
*/
- public MatroskaFileFrame getNextFrame(final int trackNo)
+ public synchronized MatroskaFileFrame getNextFrame(final int trackNo)
{
if (frameQueue.isEmpty())
{
@@ -212,10 +212,7 @@ public MatroskaFileFrame getNextFrame(final int trackNo)
frame = iter.next();
if (frame.getTrackNo() == trackNo)
{
- synchronized (frameQueue)
- {
- iter.remove();
- }
+ iter.remove();
return frame;
}
frame = null;
@@ -309,30 +306,27 @@ public long seek(final long timecode)
return 0;
}
- private void fillFrameQueue()
+ private synchronized void fillFrameQueue()
{
if (level0 == null)
{
throw new java.lang.IllegalStateException("Call readFile() before reading frames");
}
- synchronized (level0)
+ if (level1 == null)
{
- if (level1 == null)
- {
- level1 = ((MasterElement) level0).readNextChild(reader);
- }
+ level1 = ((MasterElement) level0).readNextChild(reader);
+ }
- while (frameQueue.isEmpty() && level1 != null)
+ while (frameQueue.isEmpty() && level1 != null)
+ {
+ if (level1.isType(MatroskaDocTypes.Cluster.getType()))
{
- if (level1.isType(MatroskaDocTypes.Cluster.getType()))
- {
- parseNextCluster(level1);
- }
-
- level1.skipData(ioDS);
- level1 = ((MasterElement) level0).readNextChild(reader);
+ parseNextCluster(level1);
}
+
+ level1.skipData(ioDS);
+ level1 = ((MasterElement) level0).readNextChild(reader);
}
}
@@ -365,10 +359,7 @@ else if (level2.isType(MatroskaDocTypes.SimpleBlock.getType()))
frame.setDuration(blockDuration);
frame.setData(block.getFrame(0));
frame.setKeyFrame(block.isKeyFrame());
- synchronized (frameQueue)
- {
- frameQueue.add(new MatroskaFileFrame(frame));
- }
+ frameQueue.add(new MatroskaFileFrame(frame));
if (block.getFrameCount() > 1)
{
From 03c7df0fe32d0a3bb1c77d12dc991b89eefde822 Mon Sep 17 00:00:00 2001
From: Michael Jameson
Date: Fri, 25 Oct 2019 11:37:08 -0700
Subject: [PATCH 29/42] Update spec, add parsing aids
---
src/main/java/org/ebml/EBMLReader.java | 23 +-
src/main/java/org/ebml/MasterElement.java | 45 ++-
src/main/java/org/ebml/ProtoType.java | 12 +-
.../org/ebml/matroska/MatroskaDocType.java | 315 ++++++++++++++++++
.../org/ebml/matroska/MatroskaDocTypes.java | 37 ++
tools/parseSpec.py | 34 ++
tools/specdata.xml | 171 ++++++----
7 files changed, 545 insertions(+), 92 deletions(-)
create mode 100644 src/main/java/org/ebml/matroska/MatroskaDocType.java
diff --git a/src/main/java/org/ebml/EBMLReader.java b/src/main/java/org/ebml/EBMLReader.java
index d8cd981..23c1c51 100644
--- a/src/main/java/org/ebml/EBMLReader.java
+++ b/src/main/java/org/ebml/EBMLReader.java
@@ -2,17 +2,17 @@
* JEBML - Java library to read/write EBML/Matroska elements.
* Copyright (C) 2004 Jory Stone
* Based on Javatroska (C) 2002 John Cannon
- *
+ *
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
- *
+ *
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
- *
+ *
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
@@ -120,11 +120,6 @@ public Element readNextElement()
// Read the size.
final long elementSize = readEBMLCode(source);
- if (elementSize == 0)
- {
- // Zero sized element is valid
- LOG.error("Invalid element size for {}", elem.getElementType().getName());
- }
final long end = source.getFilePointer();
// Set it's size
@@ -132,16 +127,6 @@ public Element readNextElement()
elem.setHeadersSize(end - position);
LOG.trace("Read element {} with size {}", elem.getElementType().getName(), elem.getTotalSize());
- // Setup a buffer for it's data
- // byte[] elementData = new byte[(int)elementSize];
- // Read the data
- // source.read(elementData, 0, elementData.length);
- // Set the data property on the element
- // elem.setData(elementData);
-
- // System.out.println("EBMLReader.readNextElement() returning element " + elem.getElementType().name + " with size " +
- // Long.toString(elem.getTotalSize()-elementSize)+" "+Long.toString(elementSize));
-
// Return the element
return elem;
}
@@ -230,7 +215,7 @@ public static long readEBMLCode(final DataSource source)
/**
* Takes a byte buffer and reads the bytes as an unsigned integer
- *
+ *
* @param data
* @return
*/
diff --git a/src/main/java/org/ebml/MasterElement.java b/src/main/java/org/ebml/MasterElement.java
index f618b16..fdcd8e4 100644
--- a/src/main/java/org/ebml/MasterElement.java
+++ b/src/main/java/org/ebml/MasterElement.java
@@ -2,17 +2,17 @@
* JEBML - Java library to read/write EBML/Matroska elements.
* Copyright (C) 2004 Jory Stone
* Based on Javatroska (C) 2002 John Cannon
- *
+ *
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
- *
+ *
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
- *
+ *
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
@@ -20,6 +20,7 @@
package org.ebml;
import java.util.ArrayList;
+import java.util.Iterator;
import org.ebml.io.DataSource;
import org.ebml.io.DataWriter;
@@ -52,6 +53,44 @@ public Element readNextChild(final EBMLReader reader)
return elem;
}
+ public Iterable remainingChildren(DataSource ioDS, EBMLReader reader)
+ {
+ return new Iterable()
+ {
+ @Override
+ public Iterator iterator()
+ {
+ return new Iterator()
+ {
+ Element theNext;
+ Element thePrev;
+
+ @Override
+ public boolean hasNext()
+ {
+ if (thePrev != null)
+ {
+ thePrev.skipData(ioDS);
+ }
+ if (theNext == null)
+ {
+ theNext = readNextChild(reader);
+ }
+ return theNext != null;
+ }
+
+ @Override
+ public Element next()
+ {
+ thePrev = theNext;
+ theNext = null;
+ return thePrev;
+ }
+ };
+ }
+ };
+ }
+
/* Skip the element data */
@Override
public void skipData(final DataSource source)
diff --git a/src/main/java/org/ebml/ProtoType.java b/src/main/java/org/ebml/ProtoType.java
index 9a81886..1a619a6 100644
--- a/src/main/java/org/ebml/ProtoType.java
+++ b/src/main/java/org/ebml/ProtoType.java
@@ -15,6 +15,7 @@ public class ProtoType
private final String name;
private final int level;
+ private long typeCode;
public ProtoType(final Class clazz, final String name, final byte[] type, final int level)
{
@@ -22,9 +23,9 @@ public ProtoType(final Class clazz, final String name, final byte[] type, fin
this.type = ByteBuffer.wrap(type);
this.name = name;
this.level = level;
- final long codename = EBMLReader.parseEBMLCode(this.type);
- CLASS_MAP.put(codename, this);
- LOG.trace("Associating {} with {}", name, codename);
+ typeCode = EBMLReader.parseEBMLCode(this.type);
+ CLASS_MAP.put(typeCode, this);
+ LOG.trace("Associating {} with {}", name, typeCode);
}
public T getInstance()
@@ -66,4 +67,9 @@ public ByteBuffer getType()
{
return type.asReadOnlyBuffer();
}
+
+ public long getTypeCode()
+ {
+ return typeCode;
+ }
}
diff --git a/src/main/java/org/ebml/matroska/MatroskaDocType.java b/src/main/java/org/ebml/matroska/MatroskaDocType.java
new file mode 100644
index 0000000..b820120
--- /dev/null
+++ b/src/main/java/org/ebml/matroska/MatroskaDocType.java
@@ -0,0 +1,315 @@
+package org.ebml.matroska;
+
+import java.nio.ByteBuffer;
+import java.util.HashMap;
+
+import org.ebml.EBMLReader;
+import org.ebml.Element;
+/** Matroska spec generated element list document
+ Do not manually edit this file.
+*/
+import org.ebml.ProtoType;
+
+public enum MatroskaDocType
+{
+ EBML(MatroskaDocTypes.EBML),
+ EBMLVersion(MatroskaDocTypes.EBMLVersion),
+ EBMLReadVersion(MatroskaDocTypes.EBMLReadVersion),
+ EBMLMaxIDLength(MatroskaDocTypes.EBMLMaxIDLength),
+ EBMLMaxSizeLength(MatroskaDocTypes.EBMLMaxSizeLength),
+ DocType(MatroskaDocTypes.DocType),
+ DocTypeVersion(MatroskaDocTypes.DocTypeVersion),
+ DocTypeReadVersion(MatroskaDocTypes.DocTypeReadVersion),
+ Void(MatroskaDocTypes.Void),
+ CRC_32(MatroskaDocTypes.CRC_32),
+ SignatureSlot(MatroskaDocTypes.SignatureSlot),
+ SignatureAlgo(MatroskaDocTypes.SignatureAlgo),
+ SignatureHash(MatroskaDocTypes.SignatureHash),
+ SignaturePublicKey(MatroskaDocTypes.SignaturePublicKey),
+ Signature(MatroskaDocTypes.Signature),
+ SignatureElements(MatroskaDocTypes.SignatureElements),
+ SignatureElementList(MatroskaDocTypes.SignatureElementList),
+ SignedElement(MatroskaDocTypes.SignedElement),
+ Segment(MatroskaDocTypes.Segment),
+ SeekHead(MatroskaDocTypes.SeekHead),
+ Seek(MatroskaDocTypes.Seek),
+ SeekID(MatroskaDocTypes.SeekID),
+ SeekPosition(MatroskaDocTypes.SeekPosition),
+ Info(MatroskaDocTypes.Info),
+ SegmentUID(MatroskaDocTypes.SegmentUID),
+ SegmentFilename(MatroskaDocTypes.SegmentFilename),
+ PrevUID(MatroskaDocTypes.PrevUID),
+ PrevFilename(MatroskaDocTypes.PrevFilename),
+ NextUID(MatroskaDocTypes.NextUID),
+ NextFilename(MatroskaDocTypes.NextFilename),
+ SegmentFamily(MatroskaDocTypes.SegmentFamily),
+ ChapterTranslate(MatroskaDocTypes.ChapterTranslate),
+ ChapterTranslateEditionUID(MatroskaDocTypes.ChapterTranslateEditionUID),
+ ChapterTranslateCodec(MatroskaDocTypes.ChapterTranslateCodec),
+ ChapterTranslateID(MatroskaDocTypes.ChapterTranslateID),
+ TimecodeScale(MatroskaDocTypes.TimecodeScale),
+ Duration(MatroskaDocTypes.Duration),
+ DateUTC(MatroskaDocTypes.DateUTC),
+ Title(MatroskaDocTypes.Title),
+ MuxingApp(MatroskaDocTypes.MuxingApp),
+ WritingApp(MatroskaDocTypes.WritingApp),
+ Cluster(MatroskaDocTypes.Cluster),
+ Timecode(MatroskaDocTypes.Timecode),
+ SilentTracks(MatroskaDocTypes.SilentTracks),
+ SilentTrackNumber(MatroskaDocTypes.SilentTrackNumber),
+ Position(MatroskaDocTypes.Position),
+ PrevSize(MatroskaDocTypes.PrevSize),
+ SimpleBlock(MatroskaDocTypes.SimpleBlock),
+ BlockGroup(MatroskaDocTypes.BlockGroup),
+ Block(MatroskaDocTypes.Block),
+ BlockVirtual(MatroskaDocTypes.BlockVirtual),
+ BlockAdditions(MatroskaDocTypes.BlockAdditions),
+ BlockMore(MatroskaDocTypes.BlockMore),
+ BlockAddID(MatroskaDocTypes.BlockAddID),
+ BlockAdditional(MatroskaDocTypes.BlockAdditional),
+ BlockDuration(MatroskaDocTypes.BlockDuration),
+ ReferencePriority(MatroskaDocTypes.ReferencePriority),
+ ReferenceBlock(MatroskaDocTypes.ReferenceBlock),
+ ReferenceVirtual(MatroskaDocTypes.ReferenceVirtual),
+ CodecState(MatroskaDocTypes.CodecState),
+ DiscardPadding(MatroskaDocTypes.DiscardPadding),
+ Slices(MatroskaDocTypes.Slices),
+ TimeSlice(MatroskaDocTypes.TimeSlice),
+ LaceNumber(MatroskaDocTypes.LaceNumber),
+ FrameNumber(MatroskaDocTypes.FrameNumber),
+ BlockAdditionID(MatroskaDocTypes.BlockAdditionID),
+ Delay(MatroskaDocTypes.Delay),
+ SliceDuration(MatroskaDocTypes.SliceDuration),
+ ReferenceFrame(MatroskaDocTypes.ReferenceFrame),
+ ReferenceOffset(MatroskaDocTypes.ReferenceOffset),
+ ReferenceTimeCode(MatroskaDocTypes.ReferenceTimeCode),
+ EncryptedBlock(MatroskaDocTypes.EncryptedBlock),
+ Tracks(MatroskaDocTypes.Tracks),
+ TrackEntry(MatroskaDocTypes.TrackEntry),
+ TrackNumber(MatroskaDocTypes.TrackNumber),
+ TrackUID(MatroskaDocTypes.TrackUID),
+ TrackType(MatroskaDocTypes.TrackType),
+ FlagEnabled(MatroskaDocTypes.FlagEnabled),
+ FlagDefault(MatroskaDocTypes.FlagDefault),
+ FlagForced(MatroskaDocTypes.FlagForced),
+ FlagLacing(MatroskaDocTypes.FlagLacing),
+ MinCache(MatroskaDocTypes.MinCache),
+ MaxCache(MatroskaDocTypes.MaxCache),
+ DefaultDuration(MatroskaDocTypes.DefaultDuration),
+ DefaultDecodedFieldDuration(MatroskaDocTypes.DefaultDecodedFieldDuration),
+ TrackTimecodeScale(MatroskaDocTypes.TrackTimecodeScale),
+ TrackOffset(MatroskaDocTypes.TrackOffset),
+ MaxBlockAdditionID(MatroskaDocTypes.MaxBlockAdditionID),
+ Name(MatroskaDocTypes.Name),
+ Language(MatroskaDocTypes.Language),
+ LanguageIETF(MatroskaDocTypes.LanguageIETF),
+ CodecID(MatroskaDocTypes.CodecID),
+ CodecPrivate(MatroskaDocTypes.CodecPrivate),
+ CodecName(MatroskaDocTypes.CodecName),
+ AttachmentLink(MatroskaDocTypes.AttachmentLink),
+ CodecSettings(MatroskaDocTypes.CodecSettings),
+ CodecInfoURL(MatroskaDocTypes.CodecInfoURL),
+ CodecDownloadURL(MatroskaDocTypes.CodecDownloadURL),
+ CodecDecodeAll(MatroskaDocTypes.CodecDecodeAll),
+ TrackOverlay(MatroskaDocTypes.TrackOverlay),
+ CodecDelay(MatroskaDocTypes.CodecDelay),
+ SeekPreRoll(MatroskaDocTypes.SeekPreRoll),
+ TrackTranslate(MatroskaDocTypes.TrackTranslate),
+ TrackTranslateEditionUID(MatroskaDocTypes.TrackTranslateEditionUID),
+ TrackTranslateCodec(MatroskaDocTypes.TrackTranslateCodec),
+ TrackTranslateTrackID(MatroskaDocTypes.TrackTranslateTrackID),
+ Video(MatroskaDocTypes.Video),
+ FlagInterlaced(MatroskaDocTypes.FlagInterlaced),
+ FieldOrder(MatroskaDocTypes.FieldOrder),
+ StereoMode(MatroskaDocTypes.StereoMode),
+ AlphaMode(MatroskaDocTypes.AlphaMode),
+ OldStereoMode(MatroskaDocTypes.OldStereoMode),
+ PixelWidth(MatroskaDocTypes.PixelWidth),
+ PixelHeight(MatroskaDocTypes.PixelHeight),
+ PixelCropBottom(MatroskaDocTypes.PixelCropBottom),
+ PixelCropTop(MatroskaDocTypes.PixelCropTop),
+ PixelCropLeft(MatroskaDocTypes.PixelCropLeft),
+ PixelCropRight(MatroskaDocTypes.PixelCropRight),
+ DisplayWidth(MatroskaDocTypes.DisplayWidth),
+ DisplayHeight(MatroskaDocTypes.DisplayHeight),
+ DisplayUnit(MatroskaDocTypes.DisplayUnit),
+ AspectRatioType(MatroskaDocTypes.AspectRatioType),
+ ColourSpace(MatroskaDocTypes.ColourSpace),
+ GammaValue(MatroskaDocTypes.GammaValue),
+ FrameRate(MatroskaDocTypes.FrameRate),
+ Colour(MatroskaDocTypes.Colour),
+ MatrixCoefficients(MatroskaDocTypes.MatrixCoefficients),
+ BitsPerChannel(MatroskaDocTypes.BitsPerChannel),
+ ChromaSubsamplingHorz(MatroskaDocTypes.ChromaSubsamplingHorz),
+ ChromaSubsamplingVert(MatroskaDocTypes.ChromaSubsamplingVert),
+ CbSubsamplingHorz(MatroskaDocTypes.CbSubsamplingHorz),
+ CbSubsamplingVert(MatroskaDocTypes.CbSubsamplingVert),
+ ChromaSitingHorz(MatroskaDocTypes.ChromaSitingHorz),
+ ChromaSitingVert(MatroskaDocTypes.ChromaSitingVert),
+ Range(MatroskaDocTypes.Range),
+ TransferCharacteristics(MatroskaDocTypes.TransferCharacteristics),
+ Primaries(MatroskaDocTypes.Primaries),
+ MaxCLL(MatroskaDocTypes.MaxCLL),
+ MaxFALL(MatroskaDocTypes.MaxFALL),
+ MasteringMetadata(MatroskaDocTypes.MasteringMetadata),
+ PrimaryRChromaticityX(MatroskaDocTypes.PrimaryRChromaticityX),
+ PrimaryRChromaticityY(MatroskaDocTypes.PrimaryRChromaticityY),
+ PrimaryGChromaticityX(MatroskaDocTypes.PrimaryGChromaticityX),
+ PrimaryGChromaticityY(MatroskaDocTypes.PrimaryGChromaticityY),
+ PrimaryBChromaticityX(MatroskaDocTypes.PrimaryBChromaticityX),
+ PrimaryBChromaticityY(MatroskaDocTypes.PrimaryBChromaticityY),
+ WhitePointChromaticityX(MatroskaDocTypes.WhitePointChromaticityX),
+ WhitePointChromaticityY(MatroskaDocTypes.WhitePointChromaticityY),
+ LuminanceMax(MatroskaDocTypes.LuminanceMax),
+ LuminanceMin(MatroskaDocTypes.LuminanceMin),
+ Projection(MatroskaDocTypes.Projection),
+ ProjectionType(MatroskaDocTypes.ProjectionType),
+ ProjectionPrivate(MatroskaDocTypes.ProjectionPrivate),
+ ProjectionPoseYaw(MatroskaDocTypes.ProjectionPoseYaw),
+ ProjectionPosePitch(MatroskaDocTypes.ProjectionPosePitch),
+ ProjectionPoseRoll(MatroskaDocTypes.ProjectionPoseRoll),
+ Audio(MatroskaDocTypes.Audio),
+ SamplingFrequency(MatroskaDocTypes.SamplingFrequency),
+ OutputSamplingFrequency(MatroskaDocTypes.OutputSamplingFrequency),
+ Channels(MatroskaDocTypes.Channels),
+ ChannelPositions(MatroskaDocTypes.ChannelPositions),
+ BitDepth(MatroskaDocTypes.BitDepth),
+ TrackOperation(MatroskaDocTypes.TrackOperation),
+ TrackCombinePlanes(MatroskaDocTypes.TrackCombinePlanes),
+ TrackPlane(MatroskaDocTypes.TrackPlane),
+ TrackPlaneUID(MatroskaDocTypes.TrackPlaneUID),
+ TrackPlaneType(MatroskaDocTypes.TrackPlaneType),
+ TrackJoinBlocks(MatroskaDocTypes.TrackJoinBlocks),
+ TrackJoinUID(MatroskaDocTypes.TrackJoinUID),
+ TrickTrackUID(MatroskaDocTypes.TrickTrackUID),
+ TrickTrackSegmentUID(MatroskaDocTypes.TrickTrackSegmentUID),
+ TrickTrackFlag(MatroskaDocTypes.TrickTrackFlag),
+ TrickMasterTrackUID(MatroskaDocTypes.TrickMasterTrackUID),
+ TrickMasterTrackSegmentUID(MatroskaDocTypes.TrickMasterTrackSegmentUID),
+ ContentEncodings(MatroskaDocTypes.ContentEncodings),
+ ContentEncoding(MatroskaDocTypes.ContentEncoding),
+ ContentEncodingOrder(MatroskaDocTypes.ContentEncodingOrder),
+ ContentEncodingScope(MatroskaDocTypes.ContentEncodingScope),
+ ContentEncodingType(MatroskaDocTypes.ContentEncodingType),
+ ContentCompression(MatroskaDocTypes.ContentCompression),
+ ContentCompAlgo(MatroskaDocTypes.ContentCompAlgo),
+ ContentCompSettings(MatroskaDocTypes.ContentCompSettings),
+ ContentEncryption(MatroskaDocTypes.ContentEncryption),
+ ContentEncAlgo(MatroskaDocTypes.ContentEncAlgo),
+ ContentEncKeyID(MatroskaDocTypes.ContentEncKeyID),
+ ContentEncAESSettings(MatroskaDocTypes.ContentEncAESSettings),
+ AESSettingsCipherMode(MatroskaDocTypes.AESSettingsCipherMode),
+ ContentSignature(MatroskaDocTypes.ContentSignature),
+ ContentSigKeyID(MatroskaDocTypes.ContentSigKeyID),
+ ContentSigAlgo(MatroskaDocTypes.ContentSigAlgo),
+ ContentSigHashAlgo(MatroskaDocTypes.ContentSigHashAlgo),
+ Cues(MatroskaDocTypes.Cues),
+ CuePoint(MatroskaDocTypes.CuePoint),
+ CueTime(MatroskaDocTypes.CueTime),
+ CueTrackPositions(MatroskaDocTypes.CueTrackPositions),
+ CueTrack(MatroskaDocTypes.CueTrack),
+ CueClusterPosition(MatroskaDocTypes.CueClusterPosition),
+ CueRelativePosition(MatroskaDocTypes.CueRelativePosition),
+ CueDuration(MatroskaDocTypes.CueDuration),
+ CueBlockNumber(MatroskaDocTypes.CueBlockNumber),
+ CueCodecState(MatroskaDocTypes.CueCodecState),
+ CueReference(MatroskaDocTypes.CueReference),
+ CueRefTime(MatroskaDocTypes.CueRefTime),
+ CueRefCluster(MatroskaDocTypes.CueRefCluster),
+ CueRefNumber(MatroskaDocTypes.CueRefNumber),
+ CueRefCodecState(MatroskaDocTypes.CueRefCodecState),
+ Attachments(MatroskaDocTypes.Attachments),
+ AttachedFile(MatroskaDocTypes.AttachedFile),
+ FileDescription(MatroskaDocTypes.FileDescription),
+ FileName(MatroskaDocTypes.FileName),
+ FileMimeType(MatroskaDocTypes.FileMimeType),
+ FileData(MatroskaDocTypes.FileData),
+ FileUID(MatroskaDocTypes.FileUID),
+ FileReferral(MatroskaDocTypes.FileReferral),
+ FileUsedStartTime(MatroskaDocTypes.FileUsedStartTime),
+ FileUsedEndTime(MatroskaDocTypes.FileUsedEndTime),
+ Chapters(MatroskaDocTypes.Chapters),
+ EditionEntry(MatroskaDocTypes.EditionEntry),
+ EditionUID(MatroskaDocTypes.EditionUID),
+ EditionFlagHidden(MatroskaDocTypes.EditionFlagHidden),
+ EditionFlagDefault(MatroskaDocTypes.EditionFlagDefault),
+ EditionFlagOrdered(MatroskaDocTypes.EditionFlagOrdered),
+ ChapterAtom(MatroskaDocTypes.ChapterAtom),
+ ChapterUID(MatroskaDocTypes.ChapterUID),
+ ChapterStringUID(MatroskaDocTypes.ChapterStringUID),
+ ChapterTimeStart(MatroskaDocTypes.ChapterTimeStart),
+ ChapterTimeEnd(MatroskaDocTypes.ChapterTimeEnd),
+ ChapterFlagHidden(MatroskaDocTypes.ChapterFlagHidden),
+ ChapterFlagEnabled(MatroskaDocTypes.ChapterFlagEnabled),
+ ChapterSegmentUID(MatroskaDocTypes.ChapterSegmentUID),
+ ChapterSegmentEditionUID(MatroskaDocTypes.ChapterSegmentEditionUID),
+ ChapterPhysicalEquiv(MatroskaDocTypes.ChapterPhysicalEquiv),
+ ChapterTrack(MatroskaDocTypes.ChapterTrack),
+ ChapterTrackNumber(MatroskaDocTypes.ChapterTrackNumber),
+ ChapterDisplay(MatroskaDocTypes.ChapterDisplay),
+ ChapString(MatroskaDocTypes.ChapString),
+ ChapLanguage(MatroskaDocTypes.ChapLanguage),
+ ChapLanguageIETF(MatroskaDocTypes.ChapLanguageIETF),
+ ChapCountry(MatroskaDocTypes.ChapCountry),
+ ChapProcess(MatroskaDocTypes.ChapProcess),
+ ChapProcessCodecID(MatroskaDocTypes.ChapProcessCodecID),
+ ChapProcessPrivate(MatroskaDocTypes.ChapProcessPrivate),
+ ChapProcessCommand(MatroskaDocTypes.ChapProcessCommand),
+ ChapProcessTime(MatroskaDocTypes.ChapProcessTime),
+ ChapProcessData(MatroskaDocTypes.ChapProcessData),
+ Tags(MatroskaDocTypes.Tags),
+ Tag(MatroskaDocTypes.Tag),
+ Targets(MatroskaDocTypes.Targets),
+ TargetTypeValue(MatroskaDocTypes.TargetTypeValue),
+ TargetType(MatroskaDocTypes.TargetType),
+ TagTrackUID(MatroskaDocTypes.TagTrackUID),
+ TagEditionUID(MatroskaDocTypes.TagEditionUID),
+ TagChapterUID(MatroskaDocTypes.TagChapterUID),
+ TagAttachmentUID(MatroskaDocTypes.TagAttachmentUID),
+ SimpleTag(MatroskaDocTypes.SimpleTag),
+ TagName(MatroskaDocTypes.TagName),
+ TagLanguage(MatroskaDocTypes.TagLanguage),
+ TagLanguageIETF(MatroskaDocTypes.TagLanguageIETF),
+ TagDefault(MatroskaDocTypes.TagDefault),
+ TagString(MatroskaDocTypes.TagString),
+ TagBinary(MatroskaDocTypes.TagBinary),
+ UNKNOWN(MatroskaDocTypes.Void); // Not a recognized element type
+
+ private static final HashMap CLASS_MAP = new HashMap<>();
+ static
+ {
+ for (MatroskaDocType type : values())
+ {
+ CLASS_MAP.put(type.protoType.getTypeCode(), type);
+ }
+ }
+
+ private ProtoType> protoType;
+
+ private MatroskaDocType(ProtoType> protoType)
+ {
+ this.protoType = protoType;
+ }
+
+ public ProtoType> getProtoType()
+ {
+ return protoType;
+ }
+
+ public boolean isElementThisType(Element element)
+ {
+ return element.getElementType() == protoType;
+ }
+
+ public static MatroskaDocType getType(ByteBuffer elementType)
+ {
+ long typeCode = EBMLReader.parseEBMLCode(elementType);
+ return CLASS_MAP.getOrDefault(typeCode, MatroskaDocType.UNKNOWN);
+ }
+
+ public static MatroskaDocType getType(Element element)
+ {
+ long typeCode = element.getElementType().getTypeCode();
+ return CLASS_MAP.getOrDefault(typeCode, MatroskaDocType.UNKNOWN);
+ }
+}
diff --git a/src/main/java/org/ebml/matroska/MatroskaDocTypes.java b/src/main/java/org/ebml/matroska/MatroskaDocTypes.java
index 8cc09de..b911d7a 100644
--- a/src/main/java/org/ebml/matroska/MatroskaDocTypes.java
+++ b/src/main/java/org/ebml/matroska/MatroskaDocTypes.java
@@ -105,6 +105,7 @@ public final class MatroskaDocTypes
public static final ProtoType MaxBlockAdditionID = new ProtoType<>(UnsignedIntegerElement.class, "MaxBlockAdditionID", new byte[] {(byte) 0x55, (byte) 0xEE }, 3);
public static final ProtoType Name = new ProtoType<>(UTF8StringElement.class, "Name", new byte[] {(byte) 0x53, (byte) 0x6E }, 3);
public static final ProtoType Language = new ProtoType<>(StringElement.class, "Language", new byte[] {(byte) 0x22, (byte) 0xB5, (byte) 0x9C }, 3);
+ public static final ProtoType LanguageIETF = new ProtoType<>(StringElement.class, "LanguageIETF", new byte[] {(byte) 0x22, (byte) 0xB5, (byte) 0x9D }, 3);
public static final ProtoType CodecID = new ProtoType<>(StringElement.class, "CodecID", new byte[] {(byte) 0x86 }, 3);
public static final ProtoType CodecPrivate = new ProtoType<>(BinaryElement.class, "CodecPrivate", new byte[] {(byte) 0x63, (byte) 0xA2 }, 3);
public static final ProtoType CodecName = new ProtoType<>(UTF8StringElement.class, "CodecName", new byte[] {(byte) 0x25, (byte) 0x86, (byte) 0x88 }, 3);
@@ -122,6 +123,7 @@ public final class MatroskaDocTypes
public static final ProtoType TrackTranslateTrackID = new ProtoType<>(BinaryElement.class, "TrackTranslateTrackID", new byte[] {(byte) 0x66, (byte) 0xA5 }, 4);
public static final ProtoType Video = new ProtoType<>(MasterElement.class, "Video", new byte[] {(byte) 0xE0 }, 3);
public static final ProtoType FlagInterlaced = new ProtoType<>(UnsignedIntegerElement.class, "FlagInterlaced", new byte[] {(byte) 0x9A }, 4);
+ public static final ProtoType FieldOrder = new ProtoType<>(UnsignedIntegerElement.class, "FieldOrder", new byte[] {(byte) 0x9D }, 4);
public static final ProtoType StereoMode = new ProtoType<>(UnsignedIntegerElement.class, "StereoMode", new byte[] {(byte) 0x53, (byte) 0xB8 }, 4);
public static final ProtoType AlphaMode = new ProtoType<>(UnsignedIntegerElement.class, "AlphaMode", new byte[] {(byte) 0x53, (byte) 0xC0 }, 4);
public static final ProtoType OldStereoMode = new ProtoType<>(UnsignedIntegerElement.class, "OldStereoMode", new byte[] {(byte) 0x53, (byte) 0xB9 }, 4);
@@ -138,6 +140,37 @@ public final class MatroskaDocTypes
public static final ProtoType ColourSpace = new ProtoType<>(BinaryElement.class, "ColourSpace", new byte[] {(byte) 0x2E, (byte) 0xB5, (byte) 0x24 }, 4);
public static final ProtoType GammaValue = new ProtoType<>(FloatElement.class, "GammaValue", new byte[] {(byte) 0x2F, (byte) 0xB5, (byte) 0x23 }, 4);
public static final ProtoType FrameRate = new ProtoType<>(FloatElement.class, "FrameRate", new byte[] {(byte) 0x23, (byte) 0x83, (byte) 0xE3 }, 4);
+ public static final ProtoType Colour = new ProtoType<>(MasterElement.class, "Colour", new byte[] {(byte) 0x55, (byte) 0xB0 }, 4);
+ public static final ProtoType MatrixCoefficients = new ProtoType<>(UnsignedIntegerElement.class, "MatrixCoefficients", new byte[] {(byte) 0x55, (byte) 0xB1 }, 5);
+ public static final ProtoType BitsPerChannel = new ProtoType<>(UnsignedIntegerElement.class, "BitsPerChannel", new byte[] {(byte) 0x55, (byte) 0xB2 }, 5);
+ public static final ProtoType ChromaSubsamplingHorz = new ProtoType<>(UnsignedIntegerElement.class, "ChromaSubsamplingHorz", new byte[] {(byte) 0x55, (byte) 0xB3 }, 5);
+ public static final ProtoType ChromaSubsamplingVert = new ProtoType<>(UnsignedIntegerElement.class, "ChromaSubsamplingVert", new byte[] {(byte) 0x55, (byte) 0xB4 }, 5);
+ public static final ProtoType CbSubsamplingHorz = new ProtoType<>(UnsignedIntegerElement.class, "CbSubsamplingHorz", new byte[] {(byte) 0x55, (byte) 0xB5 }, 5);
+ public static final ProtoType CbSubsamplingVert = new ProtoType<>(UnsignedIntegerElement.class, "CbSubsamplingVert", new byte[] {(byte) 0x55, (byte) 0xB6 }, 5);
+ public static final ProtoType ChromaSitingHorz = new ProtoType<>(UnsignedIntegerElement.class, "ChromaSitingHorz", new byte[] {(byte) 0x55, (byte) 0xB7 }, 5);
+ public static final ProtoType ChromaSitingVert = new ProtoType<>(UnsignedIntegerElement.class, "ChromaSitingVert", new byte[] {(byte) 0x55, (byte) 0xB8 }, 5);
+ public static final ProtoType Range = new ProtoType<>(UnsignedIntegerElement.class, "Range", new byte[] {(byte) 0x55, (byte) 0xB9 }, 5);
+ public static final ProtoType TransferCharacteristics = new ProtoType<>(UnsignedIntegerElement.class, "TransferCharacteristics", new byte[] {(byte) 0x55, (byte) 0xBA }, 5);
+ public static final ProtoType Primaries = new ProtoType<>(UnsignedIntegerElement.class, "Primaries", new byte[] {(byte) 0x55, (byte) 0xBB }, 5);
+ public static final ProtoType MaxCLL = new ProtoType<>(UnsignedIntegerElement.class, "MaxCLL", new byte[] {(byte) 0x55, (byte) 0xBC }, 5);
+ public static final ProtoType MaxFALL = new ProtoType<>(UnsignedIntegerElement.class, "MaxFALL", new byte[] {(byte) 0x55, (byte) 0xBD }, 5);
+ public static final ProtoType MasteringMetadata = new ProtoType<>(MasterElement.class, "MasteringMetadata", new byte[] {(byte) 0x55, (byte) 0xD0 }, 5);
+ public static final ProtoType PrimaryRChromaticityX = new ProtoType<>(FloatElement.class, "PrimaryRChromaticityX", new byte[] {(byte) 0x55, (byte) 0xD1 }, 6);
+ public static final ProtoType PrimaryRChromaticityY = new ProtoType<>(FloatElement.class, "PrimaryRChromaticityY", new byte[] {(byte) 0x55, (byte) 0xD2 }, 6);
+ public static final ProtoType PrimaryGChromaticityX = new ProtoType<>(FloatElement.class, "PrimaryGChromaticityX", new byte[] {(byte) 0x55, (byte) 0xD3 }, 6);
+ public static final ProtoType PrimaryGChromaticityY = new ProtoType<>(FloatElement.class, "PrimaryGChromaticityY", new byte[] {(byte) 0x55, (byte) 0xD4 }, 6);
+ public static final ProtoType PrimaryBChromaticityX = new ProtoType<>(FloatElement.class, "PrimaryBChromaticityX", new byte[] {(byte) 0x55, (byte) 0xD5 }, 6);
+ public static final ProtoType PrimaryBChromaticityY = new ProtoType<>(FloatElement.class, "PrimaryBChromaticityY", new byte[] {(byte) 0x55, (byte) 0xD6 }, 6);
+ public static final ProtoType WhitePointChromaticityX = new ProtoType<>(FloatElement.class, "WhitePointChromaticityX", new byte[] {(byte) 0x55, (byte) 0xD7 }, 6);
+ public static final ProtoType WhitePointChromaticityY = new ProtoType<>(FloatElement.class, "WhitePointChromaticityY", new byte[] {(byte) 0x55, (byte) 0xD8 }, 6);
+ public static final ProtoType LuminanceMax = new ProtoType<>(FloatElement.class, "LuminanceMax", new byte[] {(byte) 0x55, (byte) 0xD9 }, 6);
+ public static final ProtoType LuminanceMin = new ProtoType<>(FloatElement.class, "LuminanceMin", new byte[] {(byte) 0x55, (byte) 0xDA }, 6);
+ public static final ProtoType Projection = new ProtoType<>(MasterElement.class, "Projection", new byte[] {(byte) 0x76, (byte) 0x70 }, 4);
+ public static final ProtoType ProjectionType = new ProtoType<>(UnsignedIntegerElement.class, "ProjectionType", new byte[] {(byte) 0x76, (byte) 0x71 }, 5);
+ public static final ProtoType ProjectionPrivate = new ProtoType<>(BinaryElement.class, "ProjectionPrivate", new byte[] {(byte) 0x76, (byte) 0x72 }, 5);
+ public static final ProtoType ProjectionPoseYaw = new ProtoType<>(FloatElement.class, "ProjectionPoseYaw", new byte[] {(byte) 0x76, (byte) 0x73 }, 5);
+ public static final ProtoType ProjectionPosePitch = new ProtoType<>(FloatElement.class, "ProjectionPosePitch", new byte[] {(byte) 0x76, (byte) 0x74 }, 5);
+ public static final ProtoType ProjectionPoseRoll = new ProtoType<>(FloatElement.class, "ProjectionPoseRoll", new byte[] {(byte) 0x76, (byte) 0x75 }, 5);
public static final ProtoType Audio = new ProtoType<>(MasterElement.class, "Audio", new byte[] {(byte) 0xE1 }, 3);
public static final ProtoType SamplingFrequency = new ProtoType<>(FloatElement.class, "SamplingFrequency", new byte[] {(byte) 0xB5 }, 4);
public static final ProtoType OutputSamplingFrequency = new ProtoType<>(FloatElement.class, "OutputSamplingFrequency", new byte[] {(byte) 0x78, (byte) 0xB5 }, 4);
@@ -167,6 +200,8 @@ public final class MatroskaDocTypes
public static final ProtoType ContentEncryption = new ProtoType<>(MasterElement.class, "ContentEncryption", new byte[] {(byte) 0x50, (byte) 0x35 }, 5);
public static final ProtoType ContentEncAlgo = new ProtoType<>(UnsignedIntegerElement.class, "ContentEncAlgo", new byte[] {(byte) 0x47, (byte) 0xE1 }, 6);
public static final ProtoType ContentEncKeyID = new ProtoType<>(BinaryElement.class, "ContentEncKeyID", new byte[] {(byte) 0x47, (byte) 0xE2 }, 6);
+ public static final ProtoType ContentEncAESSettings = new ProtoType<>(MasterElement.class, "ContentEncAESSettings", new byte[] {(byte) 0x47, (byte) 0xE7 }, 6);
+ public static final ProtoType AESSettingsCipherMode = new ProtoType<>(UnsignedIntegerElement.class, "AESSettingsCipherMode", new byte[] {(byte) 0x47, (byte) 0xE8 }, 6);
public static final ProtoType ContentSignature = new ProtoType<>(BinaryElement.class, "ContentSignature", new byte[] {(byte) 0x47, (byte) 0xE3 }, 6);
public static final ProtoType ContentSigKeyID = new ProtoType<>(BinaryElement.class, "ContentSigKeyID", new byte[] {(byte) 0x47, (byte) 0xE4 }, 6);
public static final ProtoType ContentSigAlgo = new ProtoType<>(UnsignedIntegerElement.class, "ContentSigAlgo", new byte[] {(byte) 0x47, (byte) 0xE5 }, 6);
@@ -217,6 +252,7 @@ public final class MatroskaDocTypes
public static final ProtoType ChapterDisplay = new ProtoType<>(MasterElement.class, "ChapterDisplay", new byte[] {(byte) 0x80 }, 4);
public static final ProtoType ChapString = new ProtoType<>(UTF8StringElement.class, "ChapString", new byte[] {(byte) 0x85 }, 5);
public static final ProtoType ChapLanguage = new ProtoType<>(StringElement.class, "ChapLanguage", new byte[] {(byte) 0x43, (byte) 0x7C }, 5);
+ public static final ProtoType ChapLanguageIETF = new ProtoType<>(StringElement.class, "ChapLanguageIETF", new byte[] {(byte) 0x43, (byte) 0x7D }, 5);
public static final ProtoType ChapCountry = new ProtoType<>(StringElement.class, "ChapCountry", new byte[] {(byte) 0x43, (byte) 0x7E }, 5);
public static final ProtoType ChapProcess = new ProtoType<>(MasterElement.class, "ChapProcess", new byte[] {(byte) 0x69, (byte) 0x44 }, 4);
public static final ProtoType ChapProcessCodecID = new ProtoType<>(UnsignedIntegerElement.class, "ChapProcessCodecID", new byte[] {(byte) 0x69, (byte) 0x55 }, 5);
@@ -236,6 +272,7 @@ public final class MatroskaDocTypes
public static final ProtoType SimpleTag = new ProtoType<>(MasterElement.class, "SimpleTag", new byte[] {(byte) 0x67, (byte) 0xC8 }, 3);
public static final ProtoType TagName = new ProtoType<>(UTF8StringElement.class, "TagName", new byte[] {(byte) 0x45, (byte) 0xA3 }, 4);
public static final ProtoType TagLanguage = new ProtoType<>(StringElement.class, "TagLanguage", new byte[] {(byte) 0x44, (byte) 0x7A }, 4);
+ public static final ProtoType TagLanguageIETF = new ProtoType<>(StringElement.class, "TagLanguageIETF", new byte[] {(byte) 0x44, (byte) 0x7B }, 4);
public static final ProtoType TagDefault = new ProtoType<>(UnsignedIntegerElement.class, "TagDefault", new byte[] {(byte) 0x44, (byte) 0x84 }, 4);
public static final ProtoType TagString = new ProtoType<>(UTF8StringElement.class, "TagString", new byte[] {(byte) 0x44, (byte) 0x87 }, 4);
public static final ProtoType TagBinary = new ProtoType<>(BinaryElement.class, "TagBinary", new byte[] {(byte) 0x44, (byte) 0x85 }, 4);
diff --git a/tools/parseSpec.py b/tools/parseSpec.py
index 7299bc6..3f5858a 100644
--- a/tools/parseSpec.py
+++ b/tools/parseSpec.py
@@ -46,3 +46,37 @@
{
}
}"""
+
+print """package org.ebml.matroska;
+/** Matroska spec generated element list document
+ Do not manually edit this file.
+*/
+import org.ebml.ProtoType;
+import org.ebml.Element;
+
+public enum MatroskaDocType
+{"""
+elementFormat = ' {0}(MatroskaDocTypes.{0}),'
+for element in root.iter("element"):
+ elemName = element.attrib['name'].replace('-','_')
+ print elementFormat.format(elemName)
+
+print """ UNKNOWN(MatroskaDocTypes.Void); // Not a recognized element type
+
+ private ProtoType> protoType;
+
+ private MatroskaDocType(ProtoType> protoType)
+ {
+ this.protoType = protoType;
+ }
+
+ public ProtoType> getProtoType()
+ {
+ return protoType;
+ }
+
+ public boolean isElementThisType(Element element)
+ {
+ return element.isType(protoType.getType());
+ }
+}"""
\ No newline at end of file
diff --git a/tools/specdata.xml b/tools/specdata.xml
index 57abb97..db664e0 100644
--- a/tools/specdata.xml
+++ b/tools/specdata.xml
@@ -4,50 +4,50 @@
The version of EBML parser used to create the file.
The minimum EBML version a parser has to support to read this file.
The maximum length of the IDs you'll find in this file (4 or less in Matroska).
- The maximum length of the sizes you'll find in this file (8 or less in Matroska). This does not override the element size indicated at the beginning of an element. Elements that have an indicated size which is larger than what is allowed by EBMLMaxSizeLength shall be considered invalid.
+ The maximum length of the sizes you'll find in this file (8 or less in Matroska). This does not override the Element size indicated at the beginning of an Element. Elements that have an indicated size which is larger than what is allowed by EBMLMaxSizeLength shall be considered invalid.
A string that describes the type of document that follows this EBML header. 'matroska' in our case or 'webm' for webm files.
The version of DocType interpreter used to create the file.
The minimum DocType version an interpreter has to support to read this file.
- Used to void damaged data, to avoid unexpected behaviors when using damaged data. The content is discarded. Also used to reserve space in a sub-element for later use.
- The CRC is computed on all the data of the Master element it's in. The CRC element should be the first in it's parent master for easier reading. All level 1 elements should include a CRC-32. The CRC in use is the IEEE CRC32 Little Endian
- Contain signature of some (coming) elements in the stream.
+ Used to void damaged data, to avoid unexpected behaviors when using damaged data. The content is discarded. Also used to reserve space in a sub-element for later use.
+ The CRC is computed on all the data of the Master-element it's in. The CRC Element should be the first in it's parent master for easier reading. All level 1 Elements should include a CRC-32. The CRC in use is the IEEE CRC32 Little Endian
+ Contain signature of some (coming) Elements in the stream.
Signature algorithm used (1=RSA, 2=elliptic).
Hash algorithm used (1=SHA1-160, 2=MD5).
The public key to use with the algorithm (in the case of a PKI-based signature).
The signature of the data (until a new.
- Contains elements that will be used to compute the signature.
- A list consists of a number of consecutive elements that represent one case where data is used in signature. Ex: Cluster|Block|BlockAdditional means that the BlockAdditional of all Blocks in all Clusters is used for encryption.
- An element ID whose data will be used to compute the signature.
- This element contains all other top-level (level 1) elements. Typically a Matroska file is composed of 1 segment.
- Contains the position of other level 1 elements.
- Contains a single seek entry to an EBML element.
- The binary ID corresponding to the element name.
- The position of the element in the segment in octets (0 = first level 1 element).
+ Contains Elements that will be used to compute the signature.
+ A list consists of a number of consecutive Elements that represent one case where data is used in signature. Ex: Cluster|Block|BlockAdditional means that the BlockAdditional of all Blocks in all Clusters is used for encryption.
+ An Element ID whose data will be used to compute the signature.
+ The Root Element that contains all other Top-Level Elements (Elements defined only at Level 1). A Matroska file is composed of 1 Segment.
+ Contains the position of other Top-Level Elements.
+ Contains a single seek entry to an EBML Element.
+ The binary ID corresponding to the Element name.
+ The position of the Element in the Segment in octets (0 = first level 1 Element).
Contains miscellaneous general information and statistics on the file.
- A randomly generated unique ID to identify the current segment between many others (128 bits).
- A filename corresponding to this segment.
- A unique ID to identify the previous chained segment (128 bits).
- An escaped filename corresponding to the previous segment.
- A unique ID to identify the next chained segment (128 bits).
- An escaped filename corresponding to the next segment.
- A randomly generated unique ID that all segments related to each other must use (128 bits).
- A tuple of corresponding ID used by chapter codecs to represent this segment.
- Specify an edition UID on which this correspondance applies. When not specified, it means for all editions found in the segment.
+ A randomly generated unique ID to identify the current Segment between many others (128 bits).
+ A filename corresponding to this Segment.
+ A unique ID to identify the previous chained Segment (128 bits).
+ An escaped filename corresponding to the previous Segment.
+ A unique ID to identify the next chained Segment (128 bits).
+ An escaped filename corresponding to the next Segment.
+ A randomly generated unique ID that all Segments related to each other must use (128 bits).
+ A tuple of corresponding ID used by chapter codecs to represent this Segment.
+ Specify an edition UID on which this correspondance applies. When not specified, it means for all editions found in the Segment.
The chapter codec using this ID (0: Matroska Script, 1: DVD-menu).
- The binary value used to represent this segment in the chapter codec data. The format depends on the ChapProcessCodecID used.
- Timestamp scale in nanoseconds (1.000.000 means all timestamps in the segment are expressed in milliseconds).
+ The binary value used to represent this Segment in the chapter codec data. The format depends on the ChapProcessCodecID used.
+ Timestamp scale in nanoseconds (1.000.000 means all timestamps in the Segment are expressed in milliseconds).
- Duration of the segment (based on TimecodeScale).
+ Duration of the Segment (based on TimecodeScale).
Date of the origin of timestamp (value 0), i.e. production date.
- General name of the segment.
+ General name of the Segment.
Muxing application or library ("libmatroska-0.4.3").
Writing application ("mkvmerge-0.3.3").
- The lower level element containing the (monolithic) Block structure.
+ The Top-Level Element containing the (monolithic) Block structure.
Absolute timestamp of the cluster (based on TimecodeScale).
The list of tracks that are not used in that part of the stream. It is useful when using overlay tracks on seeking. Then you should decide what track to use.
One of the track number that are not used from now on in the stream. It could change later if not specified as silent in a further Cluster.
- The Position of the Cluster in the segment (0 in live broadcast streams). It might help to resynchronise offset on damaged streams.
+ The Position of the Cluster in the Segment (0 in live broadcast streams). It might help to resynchronise offset on damaged streams.
Size of the previous Cluster, in octets. Can be useful for backward playing.
Similar to Block but without all the extra information, mostly used to reduced overhead when no extra feature is needed. (see SimpleBlock Structure)
Basic container of information containing a single Block or BlockVirtual, and information specific to that Block/VirtualBlock.
@@ -57,25 +57,25 @@
Contain the BlockAdditional and some parameters.
An ID to identify the BlockAdditional level.
Interpreted by the codec as it wishes (using the BlockAddID).
- The duration of the Block (based on TimecodeScale). This element is mandatory when DefaultDuration is set for the track (but can be omitted as other default values). When not written and with no DefaultDuration, the value is assumed to be the difference between the timestamp of this Block and the timestamp of the next Block in "display" order (not coding order). This element can be useful at the end of a Track (as there is not other Block available), or when there is a break in a track like for subtitle tracks. When set to 0 that means the frame is not a keyframe.
+ The duration of the Block (based on TimecodeScale). This Element is mandatory when DefaultDuration is set for the track (but can be omitted as other default values). When not written and with no DefaultDuration, the value is assumed to be the difference between the timestamp of this Block and the timestamp of the next Block in "display" order (not coding order). This Element can be useful at the end of a Track (as there is not other Block available), or when there is a break in a track like for subtitle tracks. When set to 0 that means the frame is not a keyframe.
This frame is referenced and has the specified cache priority. In cache only a frame of the same or higher priority can replace this frame. A value of 0 means the frame is not referenced.
Timestamp of another frame used as a reference (ie: B or P frame). The timestamp is relative to the block it's attached to.
Relative position of the data that should be in position of the virtual block.
The new codec state to use. Data interpretation is private to the codec. This information should always be referenced by a seek entry.
Duration in nanoseconds of the silent data added to the Block (padding at the end of the Block for positive value, at the beginning of the Block for negative value). The duration of DiscardPadding is not calculated in the duration of the TrackEntry and should be discarded during playback.
Contains slices description.
- Contains extra time information about the data contained in the Block. While there are a few files in the wild with this element, it is no longer in use and has been deprecated. Being able to interpret this element is not required for playback.
- The reverse number of the frame in the lace (0 is the last frame, 1 is the next to last, etc). While there are a few files in the wild with this element, it is no longer in use and has been deprecated. Being able to interpret this element is not required for playback.
+ Contains extra time information about the data contained in the Block. While there are a few files in the wild with this Element, it is no longer in use and has been deprecated. Being able to interpret this Element is not required for playback.
+ The reverse number of the frame in the lace (0 is the last frame, 1 is the next to last, etc). While there are a few files in the wild with this Element, it is no longer in use and has been deprecated. Being able to interpret this Element is not required for playback.
The number of the frame to generate from this lace with this delay (allow you to generate many frames from the same Block/Frame).
- The ID of the BlockAdditional element (0 is the main Block).
- The (scaled) delay to apply to the element.
- The (scaled) duration to apply to the element.
+ The ID of the BlockAdditional Element (0 is the main Block).
+ The (scaled) delay to apply to the Element.
+ The (scaled) duration to apply to the Element.
DivX trick track extenstions
DivX trick track extenstions
DivX trick track extenstions
Similar to SimpleBlock but the data inside the Block are Transformed (encrypt and/or signed). (see EncryptedBlock Structure)
- A top-level block of information with many tracks described.
- Describes a track with all elements.
+ A Top-Level Element of information with many tracks described.
+ Describes a track with all Elements.
The track number as used in the Block Header (using more than 127 tracks is not encouraged, though the design allows an unlimited number).
A unique ID to identify the Track. This should be kept the same when making a direct stream copy of the Track to another file.
A set of track types coded on 8 bits (1: video, 2: audio, 3: complex, 0x10: logo, 0x11: subtitle, 0x12: buttons, 0x20: control).
@@ -85,14 +85,15 @@
Set if the track may contain blocks using lacing. (1 bit)
The minimum number of frames a player should be able to cache during playback. If set to 0, the reference pseudo-cache system is not used.
The maximum cache size required to store referenced frames in and the current frame. 0 means no cache is needed.
- Number of nanoseconds (not scaled via TimecodeScale) per frame ('frame' in the Matroska sense -- one element put into a (Simple)Block).
+ Number of nanoseconds (not scaled via TimecodeScale) per frame ('frame' in the Matroska sense -- one Element put into a (Simple)Block).
The period in nanoseconds (not scaled by TimcodeScale)
between two successive fields at the output of the decoding process (see the notes)
- DEPRECATED, DO NOT USE. The scale to apply on this track to work at normal speed in relation with other tracks (mostly used to adjust video speed when the audio length differs).
+ DEPRECATED, DO NOT USE. The scale to apply on this track to work at normal speed in relation with other tracks (mostly used to adjust video speed when the audio length differs).
A value to add to the Block's Timestamp. This can be used to adjust the playback offset of a track.
The maximum value of BlockAddID. A value 0 means there is no BlockAdditions for this track.
A human-readable track name.
- Specifies the language of the track in the Matroska languages form.
+ Specifies the language of the track in the Matroska languages form. This Element MUST be ignored if the LanguageIETF Element is used in the same TrackEntry.
+ Specifies the language of the track according to BCP 47 and using the IANA Language Subtag Registry. If this Element is used, then any Language Elements used in the same TrackEntry MUST be ignored.
An ID corresponding to the codec, see the codec page for more info.
Private data only known to the codec.
A human-readable string specifying the codec.
@@ -105,29 +106,61 @@ between two successive fields at the output of the decoding process (see CodecDelay is The codec-built-in delay in nanoseconds. This value must be subtracted from each block timestamp in order to get the actual timestamp. The value should be small so the muxing of tracks with the same actual timestamp are in the same Cluster.
After a discontinuity, SeekPreRoll is the duration in nanoseconds of the data the decoder must decode before the decoded data is valid.
The track identification for the given Chapter Codec.
- Specify an edition UID on which this translation applies. When not specified, it means for all editions found in the segment.
+ Specify an edition UID on which this translation applies. When not specified, it means for all editions found in the Segment.
The chapter codec using this ID (0: Matroska Script, 1: DVD-menu).
The binary value used to represent this track in the chapter codec data. The format depends on the ChapProcessCodecID used.
Video settings.
- Set if the video is interlaced. (1 bit)
+ A flag to declare is the video is known to be progressive or interlaced and if applicable to declare details about the interlacement. (0: undetermined, 1: interlaced, 2: progressive)
+ Declare the field ordering of the video. If FlagInterlaced is not set to 1, this Element MUST be ignored. (0: Progressive, 1: Interlaced with top field display first and top field stored first, 2: Undetermined field order, 6: Interlaced with bottom field displayed first and bottom field stored first, 9: Interlaced with bottom field displayed first and top field stored first, 14: Interlaced with top field displayed first and bottom field stored first)
Stereo-3D video mode (0: mono, 1: side by side (left eye is first), 2: top-bottom (right eye is first), 3: top-bottom (left eye is first), 4: checkboard (right is first), 5: checkboard (left is first), 6: row interleaved (right is first), 7: row interleaved (left is first), 8: column interleaved (right is first), 9: column interleaved (left is first), 10: anaglyph (cyan/red), 11: side by side (right eye is first), 12: anaglyph (green/magenta), 13 both eyes laced in one Block (left eye is first), 14 both eyes laced in one Block (right eye is first)) . There are some more details on 3D support in the Specification Notes.
- Alpha Video Mode. Presence of this element indicates that the BlockAdditional element could contain Alpha data. DEPRECATED, DO NOT USE. Bogus StereoMode value used in old versions of libmatroska. (0: mono, 1: right eye, 2: left eye, 3: both eyes).
+ Alpha Video Mode. Presence of this Element indicates that the BlockAdditional Element could contain Alpha data. DEPRECATED, DO NOT USE. Bogus StereoMode value used in old versions of libmatroska. (0: mono, 1: right eye, 2: left eye, 3: both eyes).
Width of the encoded video frames in pixels.
Height of the encoded video frames in pixels.
The number of video pixels to remove at the bottom of the image (for HDTV content).
The number of video pixels to remove at the top of the image.
The number of video pixels to remove on the left of the image.
The number of video pixels to remove on the right of the image.
- Width of the video frames to display. The default value is only valid when DisplayUnit is 0.
- Height of the video frames to display. The default value is only valid when DisplayUnit is 0.
- How DisplayWidth & DisplayHeight should be interpreted (0: pixels, 1: centimeters, 2: inches, 3: Display Aspect Ratio).
+ Width of the video frames to display. Applies to the video frame after cropping (PixelCrop* Elements). The default value is only valid when DisplayUnit is 0.
+ Height of the video frames to display. Applies to the video frame after cropping (PixelCrop* Elements). The default value is only valid when DisplayUnit is 0.
+ How DisplayWidth & DisplayHeight should be interpreted (0: pixels, 1: centimeters, 2: inches, 3: Display Aspect Ratio, 4: Unknown).
Specify the possible modifications to the aspect ratio (0: free resizing, 1: keep aspect ratio, 2: fixed).
Same value as in AVI (32 bits).
- Gamma Value.
- Number of frames per second. Informational only.
+ Gamma Value.
+ Number of frames per second. Informational only.
+ Settings describing the colour format.
+ The Matrix Coefficients of the video used to derive luma and chroma values from reg, green, and blue color primaries. For clarity, the value and meanings for MatrixCoefficients are adopted from Table 4 of ISO/IEC 23001-8:2013/DCOR1. (0:GBR, 1: BT709, 2: Unspecified, 3: Reserved, 4: FCC, 5: BT470BG, 6: SMPTE 170M, 7: SMPTE 240M, 8: YCOCG, 9: BT2020 Non-constant Luminance, 10: BT2020 Constant Luminance)
+ Number of decoded bits per channel. A value of 0 indicates that the BitsPerChannel is unspecified.
+ The amount of pixels to remove in the Cr and Cb channels for every pixel not removed horizontally. Example: For video with 4:2:0 chroma subsampling, the ChromaSubsamplingHorz should be set to 1.
+ The amount of pixels to remove in the Cr and Cb channels for every pixel not removed vertically. Example: For video with 4:2:0 chroma subsampling, the ChromaSubsamplingVert should be set to 1.
+ The amount of pixels to remove in the Cb channel for every pixel not removed horizontally. This is additive with ChromaSubsamplingHorz. Example: For video with 4:2:1 chroma subsampling, the ChromaSubsamplingHorz should be set to 1 and CbSubsamplingHorz should be set to 1.
+ The amount of pixels to remove in the Cb channel for every pixel not removed vertically. This is additive with ChromaSubsamplingVert.
+ How chroma is subsampled horizontally. (0: Unspecified, 1: Left Collocated, 2: Half)
+ How chroma is subsampled vertically. (0: Unspecified, 1: Top Collocated, 2: Half)
+ Clipping of the color ranges. (0: Unspecified, 1: Broadcast Range, 2: Full range (no clipping), 3: Defined by MatrixCoefficients/TransferCharacteristics)
+ The transfer characteristics of the video. For clarity, the value and meanings for TransferCharacteristics 1-15 are adopted from Table 3 of ISO/IEC 23001-8:2013/DCOR1. TransferCharacteristics 16-18 are proposed values. (0: Reserved, 1: ITU-R BT.709, 2: Unspecified, 3: Reserved, 4: Gamma 2.2 curve, 5: Gamma 2.8 curve, 6: SMPTE 170M, 7: SMPTE 240M, 8: Linear, 9: Log, 10: Log Sqrt, 11: IEC 61966-2-4, 12: ITU-R BT.1361 Extended Colour Gamut, 13: IEC 61966-2-1, 14: ITU-R BT.2020 10 bit, 15: ITU-R BT.2020 12 bit, 16: SMPTE ST 2084, 17: SMPTE ST 428-1 18: ARIB STD-B67 (HLG))
+ The colour primaries of the video. For clarity, the value and meanings for Primaries are adopted from Table 2 of ISO/IEC 23001-8:2013/DCOR1. (0: Reserved, 1: ITU-R BT.709, 2: Unspecified, 3: Reserved, 4: ITU-R BT.470M, 5: ITU-R BT.470BG, 6: SMPTE 170M, 7: SMPTE 240M, 8: FILM, 9: ITU-R BT.2020, 10: SMPTE ST 428-1, 22: JEDEC P22 phosphors)
+ Maximum brightness of a single pixel (Maximum Content Light Level) in candelas per square meter (cd/m²).
+ Maximum brightness of a single full frame (Maximum Frame-Average Light Level) in candelas per square meter (cd/m²).
+ SMPTE 2086 mastering data.
+ Red X chromaticity coordinate as defined by CIE 1931.
+ Red Y chromaticity coordinate as defined by CIE 1931.
+ Green X chromaticity coordinate as defined by CIE 1931.
+ Green Y chromaticity coordinate as defined by CIE 1931.
+ Blue X chromaticity coordinate as defined by CIE 1931.
+ Blue Y chromaticity coordinate as defined by CIE 1931.
+ White X chromaticity coordinate as defined by CIE 1931.
+ White Y chromaticity coordinate as defined by CIE 1931.
+ Maximum luminance. Shall be represented in candelas per square meter (cd/m²).
+ Mininum luminance. Shall be represented in candelas per square meter (cd/m²).
+ Describes the video projection details. Used to render spherical and VR videos.
+ Describes the projection used for this video track.
+ Private data that only applies to a specific projection. If ProjectionType equals 0 (Rectangular), then this element must not be present. If ProjectionType equals 1 (Equirectangular), then this element must be present and contain the same binary data that would be stored inside an ISOBMFF Equirectangular Projection Box ('equi'). If ProjectionType equals 2 (Cubemap), then this element must be present and contain the same binary data that would be stored inside an ISOBMFF Cubemap Projection Box ('cbmp'). If ProjectionType equals 3 (Mesh), then this element must be present and contain the same binary data that would be stored inside an ISOBMFF Mesh Projection Box ('mshp'). Note: ISOBMFF box size and fourcc fields are not included in the binary data, but the FullBox version and flag fields are. This is to avoid redundant framing information while preserving versioning and semantics between the two container formats.
+ Specifies a yaw rotation to the projection. Value represents a clockwise rotation, in degrees, around the up vector. This rotation must be applied before any ProjectionPosePitch or ProjectionPoseRoll rotations. The value of this field should be in the -180 to 180 degree range.
+ Specifies a pitch rotation to the projection. Value represents a counter-clockwise rotation, in degrees, around the right vector. This rotation must be applied after the ProjectionPoseYaw rotation and before the ProjectionPoseRoll rotation. The value of this field should be in the -90 to 90 degree range.
+ Specifies a roll rotation to the projection. Value represents a counter-clockwise rotation, in degrees, around the forward vector. This rotation must be applied after the ProjectionPoseYaw and ProjectionPosePitch rotations. The value of this field should be in the -180 to 180 degree range.
Audio settings.
- Sampling frequency in Hz.
- Real output sampling frequency in Hz (used for SBR techniques).
+ Sampling frequency in Hz.
+ Real output sampling frequency in Hz (used for SBR techniques).
Numbers of channels in the track.
Table of horizontal angles for each successive channel, see appendix.
Bits per sample, mostly used for PCM.
@@ -145,8 +178,8 @@ between two successive fields at the output of the decoding process (see DivX trick track extenstions
Settings for several content encoding mechanisms like compression or encryption.
Settings for one content encoding like compression or encryption.
- Tells when this modification was used during encoding/muxing starting with 0 and counting upwards. The decoder/demuxer has to start with the highest order number it finds and work its way down. This value has to be unique over all ContentEncodingOrder elements in the segment.
- A bit field that describes which elements have been modified in this way. Values (big endian) can be OR'ed. Possible values:
1 - all frame contents,
2 - the track's private data,
4 - the next ContentEncoding (next ContentEncodingOrder. Either the data inside ContentCompression and/or ContentEncryption)
+ Tells when this modification was used during encoding/muxing starting with 0 and counting upwards. The decoder/demuxer has to start with the highest order number it finds and work its way down. This value has to be unique over all ContentEncodingOrder Elements in the Segment.
+ A bit field that describes which Elements have been modified in this way. Values (big endian) can be OR'ed. Possible values:
1 - all frame contents,
2 - the track's private data,
4 - the next ContentEncoding (next ContentEncodingOrder. Either the data inside ContentCompression and/or ContentEncryption)
A value describing what kind of transformation has been done. Possible values:
0 - compression,
1 - encryption
Settings describing the compression used. Must be present if the value of ContentEncodingType is 0 and absent otherwise. Each block must be decompressable even if no previous block is available in order not to prevent seeking.
The compression algorithm used. Algorithms that have been specified so far are:
0 - zlib,
1 - bzlib,
2 - lzo1x
3 - Header Stripping
@@ -154,25 +187,27 @@ between two successive fields at the output of the decoding process (see Settings describing the encryption used. Must be present if the value of ContentEncodingType is 1 and absent otherwise.
The encryption algorithm used. The value '0' means that the contents have not been encrypted but only signed. Predefined values:
1 - DES, 2 - 3DES, 3 - Twofish, 4 - Blowfish, 5 - AES
For public key algorithms this is the ID of the public key the the data was encrypted with.
+ Settings describing the encryption algorithm used. If `ContentEncAlgo` != 5 this MUST be ignored.
+ The AES cipher mode used in the encryption.
A cryptographic signature of the contents.
This is the ID of the private key the data was signed with.
The algorithm used for the signature. A value of '0' means that the contents have not been signed but only encrypted. Predefined values:
1 - RSA
The hash algorithm used for the signature. A value of '0' means that the contents have not been signed but only encrypted. Predefined values:
1 - SHA1-160
2 - MD5
- A top-level element to speed seeking access. All entries are local to the segment. Should be mandatory for non "live" streams.
- Contains all information relative to a seek point in the segment.
- Absolute timestamp according to the segment time base.
+ A Top-Level Element to speed seeking access. All entries are local to the Segment. Should be mandatory for non "live" streams.
+ Contains all information relative to a seek point in the Segment.
+ Absolute timestamp according to the Segment time base.
Contain positions for different tracks corresponding to the timestamp.
The track for which a position is given.
The position of the Cluster containing the required Block.
- The relative position of the referenced block inside the cluster with 0 being the first possible position for an element inside that cluster.
- The duration of the block according to the segment time base. If missing the track's DefaultDuration does not apply and no duration information is available in terms of the cues.
+ The relative position of the referenced block inside the cluster with 0 being the first possible position for an Element inside that cluster.
+ The duration of the block according to the Segment time base. If missing the track's DefaultDuration does not apply and no duration information is available in terms of the cues.
Number of the Block in the specified Cluster.
- The position of the Codec State corresponding to this Cue element. 0 means that the data is taken from the initial Track Entry.
+ The position of the Codec State corresponding to this Cue Element. 0 means that the data is taken from the initial Track Entry.
The Clusters containing the required referenced Blocks.
Timestamp of the referenced Block.
The Position of the Cluster containing the referenced Block.
Number of the referenced Block of Track X in the specified Cluster.
- The position of the Codec State corresponding to this referenced element. 0 means that the data is taken from the initial Track Entry.
+ The position of the Codec State corresponding to this referenced Element. 0 means that the data is taken from the initial Track Entry.
Contain attached files.
An attached file.
A human-friendly name for the attached file.
@@ -184,7 +219,7 @@ between two successive fields at the output of the decoding process (see DivX font extension
DivX font extension
A system to define basic menus and partition data. For more detailed information, look at the Chapters Explanation.
- Contains all information about a segment edition.
+ Contains all information about a Segment edition.
A unique ID to identify the edition. It's useful for tagging an edition.
If an edition is hidden (1), it should not be available to the user interface (but still to Control Tracks; see flag notes). (1 bit)
If a flag is set (1) the edition should be used as the default one. (1 bit)
@@ -196,14 +231,15 @@ between two successive fields at the output of the decoding process (see Timestamp of the end of Chapter (timestamp excluded, not scaled).
If a chapter is hidden (1), it should not be available to the user interface (but still to Control Tracks; see flag notes). (1 bit)
Specify wether the chapter is enabled. It can be enabled/disabled by a Control Track. When disabled, the movie should skip all the content between the TimeStart and TimeEnd of this chapter (see flag notes). (1 bit)
- A segment to play in place of this chapter. Edition ChapterSegmentEditionUID should be used for this segment, otherwise no edition is used.
- The EditionUID to play from the segment linked in ChapterSegmentUID.
+ A Segment to play in place of this chapter. Edition ChapterSegmentEditionUID should be used for this Segment, otherwise no edition is used.
+ The EditionUID to play from the Segment linked in ChapterSegmentUID.
Specify the physical equivalent of this ChapterAtom like "DVD" (60) or "SIDE" (50), see complete list of values.
- List of tracks on which the chapter applies. If this element is not present, all tracks apply
- UID of the Track to apply this chapter too. In the absense of a control track, choosing this chapter will select the listed Tracks and deselect unlisted tracks. Absense of this element indicates that the Chapter should be applied to any currently used Tracks.
+ List of tracks on which the chapter applies. If this Element is not present, all tracks apply
+ UID of the Track to apply this chapter too. In the absence of a control track, choosing this chapter will select the listed Tracks and deselect unlisted tracks. Absence of this Element indicates that the Chapter should be applied to any currently used Tracks.
Contains all possible strings to use for the chapter display.
Contains the string to use as the chapter atom.
- The languages corresponding to the string, in the bibliographic ISO-639-2 form.
+ The languages corresponding to the string, in the bibliographic ISO-639-2 form. This Element MUST be ignored if the ChapLanguageIETF Element is used within the same ChapterDisplay Element.
+ Specifies the language used in the ChapString according to BCP 47 and using the IANA Language Subtag Registry. If this Element is used, then any ChapLanguage Elements used in the same ChapterDisplay MUST be ignored.
The countries corresponding to the string, same 2 octets as in Internet domains.
Contains all the commands associated to the Atom.
Contains the type of the codec used for the processing. A value of 0 means native Matroska processing (to be defined), a value of 1 means the DVD command set is used. More codec IDs can be added later.
@@ -211,9 +247,9 @@ between two successive fields at the output of the decoding process (see Contains all the commands associated to the Atom.
Defines when the process command should be handled (0: during the whole chapter, 1: before starting playback, 2: after playback of the chapter).
Contains the command information. The data should be interpreted depending on the ChapProcessCodecID value. For ChapProcessCodecID = 1, the data correspond to the binary DVD cell pre/post commands.
- Element containing elements specific to Tracks/Chapters. A list of valid tags can be found here.
- Element containing elements specific to Tracks/Chapters.
- Contain all UIDs where the specified meta data apply. It is empty to describe everything in the segment.
+ Element containing Elements specific to Tracks/Chapters. A list of valid tags can be found here.
+ Element containing Elements specific to Tracks/Chapters.
+ Contain all UIDs where the specified meta data apply. It is empty to describe everything in the Segment.
A number to indicate the logical level of the target (see TargetType).
An informational string that can be used to display the logical level of the target like "ALBUM", "TRACK", "MOVIE", "CHAPTER", etc (see TargetType).
A unique ID to identify the Track(s) the tags belong to. If the value is 0 at this level, the tags apply to all tracks in the Segment.
@@ -222,7 +258,8 @@ between two successive fields at the output of the decoding process (see A unique ID to identify the Attachment(s) the tags belong to. If the value is 0 at this level, the tags apply to all the attachments in the Segment.
Contains general information about the target.
The name of the Tag that is going to be stored.
- Specifies the language of the tag specified, in the Matroska languages form.
+ Specifies the language of the tag specified, in the Matroska languages form. This Element MUST be ignored if the TagLanguageIETF Element is used within the same SimpleTag Element.
+ Specifies the language used in the TagString according to BCP 47 and using the IANA Language Subtag Registry. If this Element is used, then any TagLanguage Elements used in the same SimpleTag MUST be ignored.
Indication to know if this is the default/original language to use for the given tag. (1 bit)
The value of the Tag.
The values of the Tag if it is binary. Note that this cannot be used in the same SimpleTag as TagString.
From cf1e79a4cb1b00364dd91c275db81e9159d21bd6 Mon Sep 17 00:00:00 2001
From: Michael Jameson
Date: Mon, 28 Oct 2019 10:14:00 -0700
Subject: [PATCH 30/42] Add support for seek, reverse play
---
build.gradle | 2 +-
src/main/java/org/ebml/io/FileDataWriter.java | 10 +-
.../java/org/ebml/matroska/MatroskaFile.java | 213 ++++++++++++++++--
.../org/ebml/matroska/MatroskaFileWriter.java | 6 +-
.../ebml/matroska/MatroskaFileReadTest.java | 117 ++++++++++
5 files changed, 328 insertions(+), 20 deletions(-)
create mode 100644 src/test/java/org/ebml/matroska/MatroskaFileReadTest.java
diff --git a/build.gradle b/build.gradle
index 9ffef7f..919b0ec 100644
--- a/build.gradle
+++ b/build.gradle
@@ -4,7 +4,7 @@ apply plugin: 'findbugs'
apply plugin: 'checkstyle'
apply plugin: 'osgi'
apply plugin: 'maven-publish'
-project.version = '2.3.3'
+project.version = '2.4.0'
repositories {
mavenLocal()
diff --git a/src/main/java/org/ebml/io/FileDataWriter.java b/src/main/java/org/ebml/io/FileDataWriter.java
index 5bb2848..e286a46 100644
--- a/src/main/java/org/ebml/io/FileDataWriter.java
+++ b/src/main/java/org/ebml/io/FileDataWriter.java
@@ -2,30 +2,31 @@
* JEBML - Java library to read/write EBML/Matroska elements.
* Copyright (C) 2004 Jory Stone
* Based on Javatroska (C) 2002 John Cannon
- *
+ *
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
- *
+ *
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
- *
+ *
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package org.ebml.io;
+import java.io.Closeable;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
-public class FileDataWriter implements DataWriter
+public class FileDataWriter implements DataWriter, Closeable
{
RandomAccessFile file = null;
FileChannel fc = null;
@@ -115,6 +116,7 @@ public long seek(final long pos)
}
}
+ @Override
public void close() throws IOException
{
file.close();
diff --git a/src/main/java/org/ebml/matroska/MatroskaFile.java b/src/main/java/org/ebml/matroska/MatroskaFile.java
index 5d3122d..c4cd2fd 100644
--- a/src/main/java/org/ebml/matroska/MatroskaFile.java
+++ b/src/main/java/org/ebml/matroska/MatroskaFile.java
@@ -19,13 +19,17 @@
*/
package org.ebml.matroska;
+import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Date;
+import java.util.Deque;
+import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
-import java.util.Queue;
-import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.Map;
+import java.util.concurrent.ConcurrentLinkedDeque;
+import org.ebml.BinaryElement;
import org.ebml.DateElement;
import org.ebml.EBMLReader;
import org.ebml.Element;
@@ -63,10 +67,14 @@ public class MatroskaFile
private String writingApp;
private long timecodeScale = 1000000;
private double duration;
- private final ArrayList trackList = new ArrayList<>();
- private final ArrayList tagList = new ArrayList<>();
- private final Queue frameQueue = new ConcurrentLinkedQueue<>();
+ private final List trackList = new ArrayList<>();
+ private final List tagList = new ArrayList<>();
+ private final List cueList = new ArrayList<>();
+ private final Deque frameQueue = new ConcurrentLinkedDeque<>();
private boolean scanFirstCluster = true;
+ private long endOfSegmentHeader;
+ private Map metaSeek = new HashMap<>();
+ private int clusterReadIndex = 0;
/**
* Primary Constructor for Matroska File class.
@@ -124,6 +132,7 @@ public void readFile()
level0 = reader.readNextElement();
if (level0.isType(MatroskaDocTypes.Segment.getType()))
{
+ endOfSegmentHeader = ioDS.getFilePointer();
level1 = ((MasterElement) level0).readNextChild(reader);
LOG.debug("Got segment element");
while (level1 != null)
@@ -150,6 +159,14 @@ else if (level1.isType(MatroskaDocTypes.Tags.getType()))
{
parseTags(level1, level2);
}
+ else if (level1.isType(MatroskaDocTypes.Cues.getType()) && isSeekable())
+ {
+ parseCues((MasterElement) level1);
+ }
+ else if (level1.isType(MatroskaDocTypes.SeekHead.getType()) && isSeekable())
+ {
+ parseMetaSeek(level1);
+ }
level1.skipData(ioDS);
level1 = ((MasterElement) level0).readNextChild(reader);
@@ -162,6 +179,54 @@ else if (level1.isType(MatroskaDocTypes.Tags.getType()))
}
}
+ private void parseMetaSeek(Element level1)
+ {
+ MasterElement seekHead = (MasterElement) level1;
+ for (Element seekElem : seekHead.remainingChildren(ioDS, reader))
+ {
+ if (seekElem.isType(MatroskaDocTypes.Seek.getType()))
+ {
+ MasterElement seekElement = (MasterElement) seekElem;
+ MatroskaDocType id = MatroskaDocType.UNKNOWN;
+ long position = endOfSegmentHeader;
+ for (Element seekMarker : seekElement.remainingChildren(ioDS, reader))
+ {
+ seekMarker.readData(ioDS);
+ if (seekMarker.isType(MatroskaDocTypes.SeekID.getType()))
+ {
+ BinaryElement binElem = (BinaryElement) seekMarker;
+ ByteBuffer type = binElem.getData();
+ id = MatroskaDocType.getType(type);
+ }
+ else if (seekMarker.isType(MatroskaDocTypes.SeekPosition.getType()))
+ {
+ position += ((UnsignedIntegerElement) seekMarker).getValue();
+ }
+ }
+ metaSeek.put(id, position);
+ }
+ }
+ }
+
+ /**
+ * Get the "previous" frame. Note that calls to {@link #getPreviousFrame()} and {@link #getNextFrame()} may not be interleaved.
+ * If you change direction, you must call {@link #seek(long)} in between.
+ */
+ public synchronized MatroskaFileFrame getPreviousFrame()
+ {
+ if (frameQueue.isEmpty())
+ {
+ // Seek to the previous cluster
+ int cueIdx = clusterReadIndex;
+ FileCue cue = cueList.get(cueIdx);
+ ioDS.seek(cue.position);
+ level1 = null;
+ fillFrameQueue();
+ clusterReadIndex = cueIdx - 1;
+ }
+ return frameQueue.pollLast();
+ }
+
/**
* Get the Next MatroskaFileFrame
*
@@ -175,11 +240,7 @@ public synchronized MatroskaFileFrame getNextFrame()
}
// If FrameQueue is still empty, must be the end of the file
- if (frameQueue.isEmpty())
- {
- return null;
- }
- return frameQueue.remove();
+ return frameQueue.poll();
}
/**
@@ -231,7 +292,7 @@ public synchronized MatroskaFileFrame getNextFrame(final int trackNo)
}
catch (final RuntimeException ex)
{
- ex.printStackTrace();
+ LOG.warn("Exception while looking for next frame for track {}", trackNo, ex);
return null;
}
@@ -243,6 +304,29 @@ public boolean isSeekable()
return this.ioDS.isSeekable();
}
+ private boolean parseCuesIfNeeded()
+ {
+ if (cueList.isEmpty() && isSeekable() && metaSeek.containsKey(MatroskaDocType.Cues))
+ {
+ long origin = ioDS.getFilePointer();
+ try
+ {
+ long position = metaSeek.get(MatroskaDocType.Cues);
+ ioDS.seek(position);
+ Element cues = reader.readNextElement();
+ if (cues.isType(MatroskaDocTypes.Cues.getType()))
+ {
+ parseCues((MasterElement) cues);
+ }
+ }
+ finally
+ {
+ ioDS.seek(origin);
+ }
+ }
+ return !cueList.isEmpty();
+ }
+
/**
* Seek to the requested timecode, rescaning clusters and/or discarding frames until we reach the nearest possible timecode, rounded down.
*
@@ -301,9 +385,43 @@ public boolean isSeekable()
* @param timecode Timecode to seek to in millseconds
* @return Actual timecode we seeked to
*/
- public long seek(final long timecode)
+ public synchronized long seek(final long timecode)
{
- return 0;
+ parseCuesIfNeeded();
+ frameQueue.clear();
+ int cueIdx = binarySearchCuesByTime(0, cueList.size(), timecode);
+ FileCue cue = cueList.get(cueIdx);
+ ioDS.seek(cue.position);
+ clusterReadIndex = cueIdx;
+ level1 = null;
+ return cue.timecode;
+ }
+
+ private int binarySearchCuesByTime(int startIdx, int endIdx, long timecode)
+ {
+ if (startIdx == endIdx)
+ {
+ return startIdx;
+ }
+ int pos = (startIdx + endIdx) / 2;
+ FileCue cue = cueList.get(pos);
+ if (cue.timecode == timecode)
+ {
+ return pos;
+ }
+ if (cue.timecode > timecode)
+ {
+ if (pos == endIdx)
+ {
+ return startIdx;
+ }
+ return binarySearchCuesByTime(startIdx, pos, timecode);
+ }
+ if (pos == startIdx)
+ {
+ return startIdx;
+ }
+ return binarySearchCuesByTime(pos, endIdx, timecode);
}
private synchronized void fillFrameQueue()
@@ -323,6 +441,7 @@ private synchronized void fillFrameQueue()
if (level1.isType(MatroskaDocTypes.Cluster.getType()))
{
parseNextCluster(level1);
+ clusterReadIndex++;
}
level1.skipData(ioDS);
@@ -599,6 +718,68 @@ else if (level4.isType(MatroskaDocTypes.TagString.getType()))
return simpleTag;
}
+ private static class FileCue
+ {
+ long position;
+ long trackId;
+ long timecode;
+
+ @Override
+ public String toString()
+ {
+ return new StringBuilder(128).append("Cue{position=")
+ .append(position)
+ .append(", trackId=")
+ .append(trackId)
+ .append(", timecode=")
+ .append(timecode)
+ .append('}')
+ .toString();
+ }
+ }
+
+ private void parseCues(MasterElement level1)
+ {
+ LOG.debug("Parsing cues");
+ for (Element level2 : level1.remainingChildren(ioDS, reader))
+ {
+ if (level2.isType(MatroskaDocTypes.CuePoint.getType()))
+ {
+ MasterElement cuePoint = (MasterElement) level2;
+ FileCue cue = new FileCue();
+ for (Element level3 : cuePoint.remainingChildren(ioDS, reader))
+ {
+ MatroskaDocType level3Type = MatroskaDocType.getType(level3);
+ if (level3Type == MatroskaDocType.CueTime)
+ {
+ level3.readData(ioDS);
+ cue.timecode = ((UnsignedIntegerElement) level3).getValue();
+ }
+ else if (level3Type == MatroskaDocType.CueTrackPositions)
+ {
+ MasterElement cueTrack = (MasterElement) level3;
+ for (Element level4 : cueTrack.remainingChildren(ioDS, reader))
+ {
+ level4.readData(ioDS);
+ MatroskaDocType level4Type = MatroskaDocType.getType(level4);
+ if (level4Type == MatroskaDocType.CueTrack)
+ {
+ cue.trackId = ((UnsignedIntegerElement) level4).getValue();
+ }
+ else if (level4Type == MatroskaDocType.CueClusterPosition)
+ {
+ // Compute absolution position by adding the offset of the end of the segment header
+ cue.position = ((UnsignedIntegerElement) level4).getValue() + endOfSegmentHeader;
+ }
+ }
+ }
+ LOG.debug("Found cue {}", cue);
+ }
+ cueList.add(cue);
+ }
+ }
+ }
+
/**
* Get a String report for the Matroska file. Call readFile() before this method, else the report will be empty.
*
@@ -633,6 +814,12 @@ public String getReport()
s.write(tagList.get(t).toString());
}
+ s.write("Cue Count: " + cueList.size() + "\n");
+ for (FileCue cue : cueList)
+ {
+ s.write("\t\t" + cue);
+ }
+
s.write("End report\n");
return s.getBuffer().toString();
diff --git a/src/main/java/org/ebml/matroska/MatroskaFileWriter.java b/src/main/java/org/ebml/matroska/MatroskaFileWriter.java
index 7fd45aa..5f249e7 100644
--- a/src/main/java/org/ebml/matroska/MatroskaFileWriter.java
+++ b/src/main/java/org/ebml/matroska/MatroskaFileWriter.java
@@ -19,6 +19,7 @@
*/
package org.ebml.matroska;
+import java.io.Closeable;
import java.util.HashSet;
import java.util.Set;
@@ -32,7 +33,7 @@
/**
* Primary API entrypoint for writing Matroska files.
*/
-public class MatroskaFileWriter
+public class MatroskaFileWriter implements Closeable
{
private static final Logger LOG = LoggerFactory.getLogger(MatroskaFileWriter.class);
@@ -42,7 +43,7 @@ public class MatroskaFileWriter
private MatroskaFileCues cueData;
private MatroskaCluster cluster;
private MatroskaSegmentInfo segmentInfoElem = new MatroskaSegmentInfo();
- private MatroskaFileTracks tracks = new MatroskaFileTracks();;
+ private MatroskaFileTracks tracks = new MatroskaFileTracks();
private MatroskaFileTags tags = new MatroskaFileTags();
private Set videoTrackNumbers = new HashSet<>();
private boolean initialized = false;
@@ -277,6 +278,7 @@ public void flush()
/**
* Finalizes the file by writing the final headers, index, and flushing data to the writer.
*/
+ @Override
public void close()
{
flush();
diff --git a/src/test/java/org/ebml/matroska/MatroskaFileReadTest.java b/src/test/java/org/ebml/matroska/MatroskaFileReadTest.java
new file mode 100644
index 0000000..f2592e3
--- /dev/null
+++ b/src/test/java/org/ebml/matroska/MatroskaFileReadTest.java
@@ -0,0 +1,117 @@
+package org.ebml.matroska;
+
+import java.io.File;
+import java.nio.ByteBuffer;
+import java.util.List;
+
+import org.ebml.io.FileDataSource;
+import org.ebml.io.FileDataWriter;
+import org.ebml.matroska.MatroskaFileTrack.MatroskaVideoTrack;
+import org.ebml.matroska.MatroskaFileTrack.TrackType;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class MatroskaFileReadTest
+{
+ private static final Logger LOG = LoggerFactory.getLogger(MatroskaFileReadTest.class);
+ private static File destination;
+ private static MatroskaFileTrack testTrack;
+ private static MatroskaFileTagEntry testTag;
+ private MatroskaFile file;
+
+ @BeforeClass
+ public static void setUpClass() throws Exception
+ {
+ destination = File.createTempFile("test", ".mkv");
+ try (FileDataWriter ioDW = new FileDataWriter(destination.getPath());
+ MatroskaFileWriter writer = new MatroskaFileWriter(ioDW))
+ {
+ testTrack = new MatroskaFileTrack();
+ testTrack.setTrackNo(42);
+ testTrack.setTrackType(TrackType.VIDEO);
+ testTrack.setCodecID("V_MPEG4/ISO/AVC");
+ testTrack.setDefaultDuration(33);
+ MatroskaVideoTrack video = new MatroskaVideoTrack();
+ video.setDisplayHeight((short) 1080);
+ video.setDisplayWidth((short) 1920);
+ testTrack.setVideo(video);
+ writer.addTrack(testTrack);
+ MatroskaFileSimpleTag simpleTag = new MatroskaFileSimpleTag();
+ simpleTag.setName("TITLE");
+ simpleTag.setValue("Canon in D");
+ testTag = new MatroskaFileTagEntry();
+ testTag.addSimpleTag(simpleTag);
+ writer.addTag(testTag);
+ for (int i = 0; i < 100; ++i)
+ {
+ MatroskaFileFrame frame = new MatroskaFileFrame();
+ frame.setData(ByteBuffer.wrap(new byte[] {(byte) i }));
+ frame.setTimecode(i * 5);
+ frame.setTrackNo(42);
+ frame.setKeyFrame(i % 3 == 0);
+ writer.addFrame(frame);
+ }
+ }
+ }
+
+ @Before
+ public void setUp() throws Exception
+ {
+ FileDataSource ioDS = new FileDataSource(destination.getPath());
+ file = new MatroskaFile(ioDS);
+ file.readFile();
+ }
+
+ @Test
+ public void testGetTags()
+ {
+ List tagEntries = file.getTagList();
+ Assert.assertEquals(1, tagEntries.size());
+ List tags = tagEntries.get(0).simpleTags;
+ Assert.assertEquals(1, tags.size());
+ MatroskaFileSimpleTag tag = tags.get(0);
+ Assert.assertEquals("TITLE", tag.getName());
+ Assert.assertEquals("Canon in D", tag.getValue());
+ }
+
+ @Test
+ public void testSeek()
+ {
+ LOG.debug("Testing seek");
+ long seeked = file.seek(123);
+ MatroskaFileFrame nextFrame = file.getNextFrame();
+ Assert.assertEquals(120, seeked);
+ Assert.assertEquals(120, nextFrame.getTimecode());
+ }
+
+ @Test
+ public void testPlayThrough()
+ {
+ for (int i = 0; i < 100; ++i)
+ {
+ MatroskaFileFrame frame = file.getNextFrame();
+ Assert.assertEquals(i, frame.getData().get());
+ Assert.assertEquals(i * 5, frame.getTimecode());
+ Assert.assertEquals(42, frame.getTrackNo());
+ Assert.assertEquals(i % 3 == 0, frame.isKeyFrame());
+ }
+ }
+
+ @Test
+ public void testPlayBackwards()
+ {
+ file.seek(500);
+ for (int i = 99; i >= 0; --i)
+ {
+ MatroskaFileFrame frame = file.getPreviousFrame();
+ Assert.assertEquals(i, frame.getData().get());
+ Assert.assertEquals(i * 5, frame.getTimecode());
+ Assert.assertEquals(42, frame.getTrackNo());
+ Assert.assertEquals(i % 3 == 0, frame.isKeyFrame());
+ }
+ }
+}
From f4418c01eeccfc9c5b3fe75939fc1f0aa8f3d899 Mon Sep 17 00:00:00 2001
From: Michael Jameson
Date: Mon, 28 Oct 2019 10:24:08 -0700
Subject: [PATCH 31/42] Fix checkstyle and remove findbugs
---
build.gradle | 23 +-
.../java/org/ebml/matroska/MatroskaFile.java | 3 +-
tools/csMain.xml | 56 ++--
tools/csTest.xml | 291 ------------------
4 files changed, 31 insertions(+), 342 deletions(-)
delete mode 100644 tools/csTest.xml
diff --git a/build.gradle b/build.gradle
index 919b0ec..55f92d5 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,11 +1,11 @@
apply plugin: 'java'
apply plugin: 'maven'
-apply plugin: 'findbugs'
apply plugin: 'checkstyle'
apply plugin: 'osgi'
apply plugin: 'maven-publish'
-project.version = '2.4.0'
+
+project.version = '2.4.0'
repositories {
mavenLocal()
mavenCentral()
@@ -22,24 +22,7 @@ checkstyleMain {
configFile = file("tools/csMain.xml")
}
checkstyleTest {
- configFile = file("tools/csTest.xml")
-}
-findbugs {
- toolVersion = "3.+"
- effort = "max"
- reportLevel = "high"
-}
-findbugsMain {
- reports {
- xml.enabled = false
- html.enabled = true
- }
-}
-findbugsTest {
- reports {
- xml.enabled = false
- html.enabled = true
- }
+ configFile = file("tools/csMain.xml")
}
// Workaround so that packageinfo files get included for OSGi package versions
diff --git a/src/main/java/org/ebml/matroska/MatroskaFile.java b/src/main/java/org/ebml/matroska/MatroskaFile.java
index c4cd2fd..84f514e 100644
--- a/src/main/java/org/ebml/matroska/MatroskaFile.java
+++ b/src/main/java/org/ebml/matroska/MatroskaFile.java
@@ -59,7 +59,7 @@ public class MatroskaFile
private final DataSource ioDS;
private final EBMLReader reader;
private Element level0 = null;
- // Level1 is required fo lazy reading of frames
+ // Level1 is required for lazy reading of frames
private Element level1 = null;
private String segmentTitle;
private Date segmentDate;
@@ -94,7 +94,6 @@ public MatroskaFile(final DataSource inputDataSource)
*/
public void readFile()
{
- Element level1 = null;
final Element level2 = null;
// Element level3 = null;
// Element level4 = null;
diff --git a/tools/csMain.xml b/tools/csMain.xml
index 70ad049..7eb6485 100644
--- a/tools/csMain.xml
+++ b/tools/csMain.xml
@@ -4,13 +4,17 @@
+
-
+
@@ -29,11 +33,6 @@
-
@@ -76,11 +75,13 @@
+
@@ -107,11 +108,13 @@
+
-
+
@@ -148,23 +151,13 @@
-
-
-
-
-
@@ -196,10 +189,13 @@
+
+
@@ -235,9 +231,11 @@
+
@@ -265,6 +263,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
@@ -278,19 +288,7 @@
-
-
-
-
-
-
-
-
diff --git a/tools/csTest.xml b/tools/csTest.xml
deleted file mode 100644
index 50cd577..0000000
--- a/tools/csTest.xml
+++ /dev/null
@@ -1,291 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
From 7aa93d4e9bde89be9f1cf5484ccc2f816022fa16 Mon Sep 17 00:00:00 2001
From: Michael Jameson
Date: Mon, 28 Oct 2019 15:59:18 -0700
Subject: [PATCH 32/42] Fix issue with reverse playout when hitting end of file
---
build.gradle | 2 +-
src/main/java/org/ebml/matroska/MatroskaFile.java | 2 +-
src/test/java/org/ebml/matroska/MatroskaFileReadTest.java | 2 ++
3 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/build.gradle b/build.gradle
index 55f92d5..ccaab5f 100644
--- a/build.gradle
+++ b/build.gradle
@@ -5,7 +5,7 @@ apply plugin: 'osgi'
apply plugin: 'maven-publish'
-project.version = '2.4.0'
+project.version = '2.4.1'
repositories {
mavenLocal()
mavenCentral()
diff --git a/src/main/java/org/ebml/matroska/MatroskaFile.java b/src/main/java/org/ebml/matroska/MatroskaFile.java
index 84f514e..ec01ebd 100644
--- a/src/main/java/org/ebml/matroska/MatroskaFile.java
+++ b/src/main/java/org/ebml/matroska/MatroskaFile.java
@@ -213,7 +213,7 @@ else if (seekMarker.isType(MatroskaDocTypes.SeekPosition.getType()))
*/
public synchronized MatroskaFileFrame getPreviousFrame()
{
- if (frameQueue.isEmpty())
+ if (frameQueue.isEmpty() && clusterReadIndex >= 0)
{
// Seek to the previous cluster
int cueIdx = clusterReadIndex;
diff --git a/src/test/java/org/ebml/matroska/MatroskaFileReadTest.java b/src/test/java/org/ebml/matroska/MatroskaFileReadTest.java
index f2592e3..eaf8620 100644
--- a/src/test/java/org/ebml/matroska/MatroskaFileReadTest.java
+++ b/src/test/java/org/ebml/matroska/MatroskaFileReadTest.java
@@ -99,6 +99,7 @@ public void testPlayThrough()
Assert.assertEquals(42, frame.getTrackNo());
Assert.assertEquals(i % 3 == 0, frame.isKeyFrame());
}
+ Assert.assertNull(file.getNextFrame());
}
@Test
@@ -113,5 +114,6 @@ public void testPlayBackwards()
Assert.assertEquals(42, frame.getTrackNo());
Assert.assertEquals(i % 3 == 0, frame.isKeyFrame());
}
+ Assert.assertNull(file.getPreviousFrame());
}
}
From a158f4ebcecf9a7fc9fad74d626df5c358c4c05a Mon Sep 17 00:00:00 2001
From: Michael Jameson
Date: Tue, 29 Oct 2019 14:47:36 -0700
Subject: [PATCH 33/42] Improved logging
---
build.gradle | 2 +-
src/main/java/org/ebml/EBMLReader.java | 32 ++------------------------
src/main/java/org/ebml/ProtoType.java | 8 ++++++-
3 files changed, 10 insertions(+), 32 deletions(-)
diff --git a/build.gradle b/build.gradle
index ccaab5f..8f20837 100644
--- a/build.gradle
+++ b/build.gradle
@@ -5,7 +5,7 @@ apply plugin: 'osgi'
apply plugin: 'maven-publish'
-project.version = '2.4.1'
+project.version = '2.4.2'
repositories {
mavenLocal()
mavenCentral()
diff --git a/src/main/java/org/ebml/EBMLReader.java b/src/main/java/org/ebml/EBMLReader.java
index 23c1c51..931dc3c 100644
--- a/src/main/java/org/ebml/EBMLReader.java
+++ b/src/main/java/org/ebml/EBMLReader.java
@@ -106,7 +106,7 @@ public Element readNextElement()
if (elementType == null)
{
- // Failed to read type id
+ // Failed to read type id, most likely due to end-of-file
return null;
}
@@ -131,34 +131,6 @@ public Element readNextElement()
return elem;
}
- public static ByteBuffer getEBMLCodeAsBytes(final DataSource source)
- {
- // Begin loop with byte set to newly read byte.
- final byte firstByte = source.readByte();
- final int numBytes = readEBMLCodeSize(firstByte);
-
- if (numBytes == 0)
- {
- LOG.error("Failed to read ebml code size from {}", firstByte);
- // Invalid size
- return null;
- }
-
- // Setup space to store the bits
- final ByteBuffer buf = ByteBuffer.allocate(numBytes);
-
- // Clear the 1 at the front of this byte, all the way to the beginning of the size
- buf.put((byte) (firstByte & ((0xFF >>> (numBytes)))));
-
- if (numBytes > 1)
- {
- // Read the rest of the size.
- source.read(buf);
- }
- buf.flip();
- return buf;
- }
-
public static int readEBMLCodeSize(final byte firstByte)
{
int numBytes = 0;
@@ -401,7 +373,7 @@ public static ByteBuffer readEBMLCodeAsBytes(final DataSource source)
if (numBytes == 0)
{
- LOG.error("Failed to read ebml code size from {}", firstByte);
+ LOG.warn("Failed to read ebml code size from {} -- most likely end of file", firstByte);
// Invalid size
return null;
}
diff --git a/src/main/java/org/ebml/ProtoType.java b/src/main/java/org/ebml/ProtoType.java
index 1a619a6..3a4ca5e 100644
--- a/src/main/java/org/ebml/ProtoType.java
+++ b/src/main/java/org/ebml/ProtoType.java
@@ -48,7 +48,13 @@ public T getInstance()
public static Element getInstance(final ByteBuffer type)
{
final long codename = EBMLReader.parseEBMLCode(type);
- final ProtoType extends Element> eType = CLASS_MAP.get(Long.valueOf(codename));
+ Long codeValue = Long.valueOf(codename);
+ final ProtoType extends Element> eType = CLASS_MAP.get(codeValue);
+ if (eType == null)
+ {
+ LOG.warn("Unrecognized element type {}", Long.toHexString(codeValue));
+ return null;
+ }
LOG.trace("Got codename {}, for element type {}", codename, eType.name);
return eType.getInstance();
}
From 762109e439d8aa2a068c2f7013b9a7cdabfb7bc9 Mon Sep 17 00:00:00 2001
From: Michael Jameson
Date: Mon, 4 Nov 2019 14:35:37 -0800
Subject: [PATCH 34/42] Fix for seek in reverse not giving the desired frame
---
build.gradle | 2 +-
src/main/java/org/ebml/matroska/MatroskaFile.java | 9 ++++++++-
.../java/org/ebml/matroska/MatroskaFileReadTest.java | 4 ++--
3 files changed, 11 insertions(+), 4 deletions(-)
diff --git a/build.gradle b/build.gradle
index 8f20837..b3208a3 100644
--- a/build.gradle
+++ b/build.gradle
@@ -5,7 +5,7 @@ apply plugin: 'osgi'
apply plugin: 'maven-publish'
-project.version = '2.4.2'
+project.version = '2.4.4'
repositories {
mavenLocal()
mavenCentral()
diff --git a/src/main/java/org/ebml/matroska/MatroskaFile.java b/src/main/java/org/ebml/matroska/MatroskaFile.java
index ec01ebd..4f8afdc 100644
--- a/src/main/java/org/ebml/matroska/MatroskaFile.java
+++ b/src/main/java/org/ebml/matroska/MatroskaFile.java
@@ -382,14 +382,21 @@ private boolean parseCuesIfNeeded()
*
*
* @param timecode Timecode to seek to in millseconds
+ * @param playDirectionIsReverse true if {@link #getPreviousFrame()} will be called next instead of {@link #getNextFrame()}
* @return Actual timecode we seeked to
*/
- public synchronized long seek(final long timecode)
+ public synchronized long seek(long timecode, boolean playDirectionIsReverse)
{
parseCuesIfNeeded();
frameQueue.clear();
int cueIdx = binarySearchCuesByTime(0, cueList.size(), timecode);
FileCue cue = cueList.get(cueIdx);
+ if (playDirectionIsReverse && cue.timecode != timecode)
+ {
+ // When playing in reverse, we have to skip one additional cluster
+ // so that the play head actually crosses the seek time
+ cueIdx = Math.min(cueIdx + 1, cueList.size() - 1);
+ }
ioDS.seek(cue.position);
clusterReadIndex = cueIdx;
level1 = null;
diff --git a/src/test/java/org/ebml/matroska/MatroskaFileReadTest.java b/src/test/java/org/ebml/matroska/MatroskaFileReadTest.java
index eaf8620..622b54c 100644
--- a/src/test/java/org/ebml/matroska/MatroskaFileReadTest.java
+++ b/src/test/java/org/ebml/matroska/MatroskaFileReadTest.java
@@ -82,7 +82,7 @@ public void testGetTags()
public void testSeek()
{
LOG.debug("Testing seek");
- long seeked = file.seek(123);
+ long seeked = file.seek(123, false);
MatroskaFileFrame nextFrame = file.getNextFrame();
Assert.assertEquals(120, seeked);
Assert.assertEquals(120, nextFrame.getTimecode());
@@ -105,7 +105,7 @@ public void testPlayThrough()
@Test
public void testPlayBackwards()
{
- file.seek(500);
+ file.seek(500, true);
for (int i = 99; i >= 0; --i)
{
MatroskaFileFrame frame = file.getPreviousFrame();
From c23b58bd7da4e2c4f601d93ec4e051d45ac71f6e Mon Sep 17 00:00:00 2001
From: damencho
Date: Thu, 10 Oct 2024 16:01:04 -0500
Subject: [PATCH 35/42] feat: Adds initial maven configuration.
---
pom.xml | 88 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 88 insertions(+)
create mode 100644 pom.xml
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..92107fb
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,88 @@
+
+ 4.0.0
+
+ org.jitsi
+ jebml
+ 2.4.4-SNAPSHOT
+
+ jebml
+ Matroska muxer/demuxer
+ https://github.com/jitsi/jebml
+
+
+ jitsi.org
+ https://jitsi.org
+
+
+
+
+ org.jitsi
+ Jitsi Team
+ dev@jitsi.org
+
+
+
+
+ https://github.com/jitsi/jebml
+ scm:git:https://github.com/jitsi/jebml.git
+ scm:git:https://github.com/jitsi/jebml.git
+
+
+
+
+ The GNU Lesser General Public License, Version 2.0
+ https://github.com/jitsi/jebml/blob/master/LGPL.txt
+ repo
+
+
+
+
+ UTF-8
+
+
+
+
+ org.slf4j
+ slf4j-api
+ 1.7.5
+ provided
+
+
+
+ junit
+ junit
+ 4.11
+ test
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.10.1
+
+ 11
+
+ -Xlint:all
+
+
+
+
+ org.apache.maven.plugins
+ maven-source-plugin
+ 3.2.1
+
+
+ attach-sources
+ verify
+
+ jar-no-fork
+
+
+
+
+
+
+
From a08c4db7aff187c2889f0f59875189b548a8fe4b Mon Sep 17 00:00:00 2001
From: damencho
Date: Thu, 10 Oct 2024 16:04:44 -0500
Subject: [PATCH 36/42] feat: Adds GitHub actions build and test.
---
.github/workflows/maven.yml | 38 +++++++++++++++++++++++++++++++++++++
1 file changed, 38 insertions(+)
create mode 100644 .github/workflows/maven.yml
diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml
new file mode 100644
index 0000000..e8b496e
--- /dev/null
+++ b/.github/workflows/maven.yml
@@ -0,0 +1,38 @@
+# This workflow will build a Java project with Maven
+# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven
+
+name: Java CI with Maven
+
+on:
+ push:
+ branches: [ jitsi ]
+ pull_request:
+ branches: [ jitsi ]
+
+env:
+ # Java version to use for the release
+ RELEASE_JAVA_VERSION: 11
+
+jobs:
+ test:
+ runs-on: ubuntu-latest
+
+ strategy:
+ matrix:
+ java: [ 11, 17 ]
+
+ name: Java ${{ matrix.java }}
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v3
+
+ - name: Set up JDK ${{ matrix.java }}
+ uses: actions/setup-java@v3
+ with:
+ distribution: temurin
+ java-version: ${{ matrix.java }}
+ cache: maven
+
+ - name: Build and test with Maven
+ run: mvn -B verify
From 9c9a025a9e478016fe0757f60ed369cacf777c3e Mon Sep 17 00:00:00 2001
From: damencho
Date: Thu, 10 Oct 2024 16:31:13 -0500
Subject: [PATCH 37/42] feat: Adds release to maven central.
---
.github/workflows/maven.yml | 61 +++++++++++++++++++++++++++++++++++++
1 file changed, 61 insertions(+)
diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml
index e8b496e..f43c21a 100644
--- a/.github/workflows/maven.yml
+++ b/.github/workflows/maven.yml
@@ -36,3 +36,64 @@ jobs:
- name: Build and test with Maven
run: mvn -B verify
+
+ release:
+ if: github.ref == 'refs/heads/jitsi'
+ needs: test
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v3
+ with:
+ fetch-depth: 0
+
+ - name: Set up JDK
+ uses: actions/setup-java@v3
+ with:
+ distribution: temurin
+ java-version: ${{ env.RELEASE_JAVA_VERSION }}
+ cache: maven
+ server-id: ossrh
+ server-username: SONATYPE_USER
+ server-password: SONATYPE_PW
+
+ - name: Install xmllint
+ shell: bash
+ run: |
+ sudo apt update
+ sudo apt install -y libxml2-utils
+
+ - name: Set tag Version
+ id: sets-tag-version
+ run: |
+ MVNVER=$(xmllint --xpath "/*[local-name()='project']/*[local-name()='version']/text()" pom.xml)
+ TAG_NAME="v${MVNVER/-SNAPSHOT/}"
+ echo "Tag name: ${TAG_NAME}"
+ echo "TAG_NAME=${TAG_NAME}" >> $GITHUB_OUTPUT
+
+ - name: Create Tag
+ uses: rickstaa/action-create-tag@v1.7.2
+ with:
+ tag_exists_error: false
+ tag: ${{ steps.sets-tag-version.outputs.TAG_NAME }}
+ message: "Automated tag"
+
+ - name: Set version
+ run: |
+ VERSION=`git describe --match "v[0-9\.]*" --long --dirty --always`
+ mvn -B versions:set -DnewVersion=${VERSION:1} -DgenerateBackupPoms=false
+
+ - name: Release to Maven Central
+ env:
+ SONATYPE_USER: ${{ secrets.SONATYPE_USER }}
+ SONATYPE_PW: ${{ secrets.SONATYPE_PW }}
+ run: |
+ cat <(echo -e "${{ secrets.GPG_KEY }}") | gpg --batch --import
+ gpg --list-secret-keys --keyid-format LONG
+ mvn \
+ --no-transfer-progress \
+ --batch-mode \
+ -Dgpg.passphrase="${{ secrets.GPG_PW }}" \
+ -Drelease=true \
+ -DskipTests \
+ deploy
From 1491873c3cb1ec15abcb38a98e71ed0998653167 Mon Sep 17 00:00:00 2001
From: damencho
Date: Thu, 10 Oct 2024 16:42:15 -0500
Subject: [PATCH 38/42] fix: Adds distribution management needed by
deploy/release.
---
pom.xml | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/pom.xml b/pom.xml
index 92107fb..daa58ea 100644
--- a/pom.xml
+++ b/pom.xml
@@ -83,6 +83,22 @@
+
+ org.apache.maven.plugins
+ maven-deploy-plugin
+ 3.0.0-M2
+
+
+
+
+ ossrh
+ https://oss.sonatype.org/content/repositories/snapshots
+
+
+ ossrh
+ https://oss.sonatype.org/service/local/staging/deploy/maven2/
+
+
From 0a5139fcc0bd56c5d5caa83c17c4a63296b85784 Mon Sep 17 00:00:00 2001
From: damencho
Date: Thu, 10 Oct 2024 17:06:42 -0500
Subject: [PATCH 39/42] fix: Adds release phase.
---
pom.xml | 89 ++++++++++++++++++++++++++++++++++++++++++++++++---------
1 file changed, 75 insertions(+), 14 deletions(-)
diff --git a/pom.xml b/pom.xml
index daa58ea..1ef4635 100644
--- a/pom.xml
+++ b/pom.xml
@@ -38,6 +38,7 @@
UTF-8
+ 1.9.10
@@ -69,28 +70,88 @@
-
- org.apache.maven.plugins
- maven-source-plugin
- 3.2.1
-
-
- attach-sources
- verify
-
- jar-no-fork
-
-
-
-
org.apache.maven.plugins
maven-deploy-plugin
3.0.0-M2
+
+ org.sonatype.plugins
+ nexus-staging-maven-plugin
+ 1.6.13
+ true
+
+ ossrh
+ https://oss.sonatype.org/
+ true
+
+
+
+
+ release
+
+
+ release
+ true
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-source-plugin
+ 3.2.1
+
+
+ attach-sources
+ verify
+
+ jar-no-fork
+
+
+
+
+
+ org.jetbrains.dokka
+ dokka-maven-plugin
+ ${dokka.version}
+
+
+ package
+
+ javadocJar
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-gpg-plugin
+ 3.0.1
+
+
+ sign-artifacts
+ verify
+
+ sign
+
+
+
+
+
+ --pinentry-mode
+ loopback
+
+
+
+
+
+
+
+
ossrh
From 1299842908ac4e59576eb5eae5795f22688a521b Mon Sep 17 00:00:00 2001
From: Boris Grozev
Date: Tue, 15 Oct 2024 18:17:38 -0500
Subject: [PATCH 40/42] Fix cluster isFlushNeeded (don't create a cluster for
each frame).
---
src/main/java/org/ebml/matroska/MatroskaCluster.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/main/java/org/ebml/matroska/MatroskaCluster.java b/src/main/java/org/ebml/matroska/MatroskaCluster.java
index 01e3054..49bfbba 100644
--- a/src/main/java/org/ebml/matroska/MatroskaCluster.java
+++ b/src/main/java/org/ebml/matroska/MatroskaCluster.java
@@ -76,7 +76,7 @@ public void addFrame(final MatroskaFileFrame frame)
public boolean isFlushNeeded()
{
- return (lastTimecode - clusterTimecode) < durationLimit;
+ return (lastTimecode - clusterTimecode) > durationLimit;
}
public long flush(final DataWriter ioDW)
From 09e9281937323faf5af19f0be09a1d3c5969f37d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=D0=94=D0=B0=D0=BC=D1=8F=D0=BD=20=D0=9C=D0=B8=D0=BD=D0=BA?=
=?UTF-8?q?=D0=BE=D0=B2?=
Date: Mon, 21 Oct 2024 14:05:15 -0500
Subject: [PATCH 41/42] Various fixes (#3)
* feat: Sync with matroska v4 spec.
* fix: Skips adding some element if no data was provided.
* fix: Fix adding all tracks in the cue.
We used to add only the track of the current frame when flushing, so it does not guarantee all tracks inclusion. With current approach we will have in a cluster all tracks on their first frame added.
* fix: Fixes updating segment size on closing file.
* fix: Updates tags entry creation.
* feat: Adds duration in segment.
* fix: Fixes Date computation.
---
src/main/java/org/ebml/DateElement.java | 10 +--
.../org/ebml/matroska/MatroskaDocType.java | 24 ++++---
.../org/ebml/matroska/MatroskaDocTypes.java | 24 ++++---
.../java/org/ebml/matroska/MatroskaFile.java | 7 +-
.../org/ebml/matroska/MatroskaFileCues.java | 11 +--
.../ebml/matroska/MatroskaFileMetaSeek.java | 6 +-
.../ebml/matroska/MatroskaFileTagEntry.java | 22 ++++++
.../org/ebml/matroska/MatroskaFileTags.java | 5 +-
.../org/ebml/matroska/MatroskaFileTrack.java | 63 +++++++++++------
.../org/ebml/matroska/MatroskaFileTracks.java | 5 +-
.../org/ebml/matroska/MatroskaFileWriter.java | 68 +++++++++----------
.../org/ebml/matroska/MatroskaSegment.java | 13 +++-
.../ebml/matroska/MatroskaSegmentInfo.java | 11 +--
13 files changed, 172 insertions(+), 97 deletions(-)
diff --git a/src/main/java/org/ebml/DateElement.java b/src/main/java/org/ebml/DateElement.java
index 72d0753..77de5ca 100644
--- a/src/main/java/org/ebml/DateElement.java
+++ b/src/main/java/org/ebml/DateElement.java
@@ -25,7 +25,7 @@
public class DateElement extends SignedIntegerElement
{
// const uint64 EbmlDate::UnixEpochDelay = 978307200; // 2001/01/01 00:00:00 UTC
- public static final long UnixEpochDelay = 978307200; // 2001/01/01 00:00:00 UTC
+ public static final long UnixEpochDelay = 978307200000L; // 2001/01/01 00:00:00 UTC
private static final int MIN_SIZE_LENGTH = 8;
public DateElement(final byte[] type)
@@ -40,18 +40,18 @@ public DateElement()
/**
* Set the Date of this element
- *
+ *
* @param value Date to set
*/
public void setDate(final Date value)
{
- final long val = (value.getTime() - UnixEpochDelay) * 1000000000;
+ final long val = (value.getTime() - UnixEpochDelay) * 1000000;
setData(ByteBuffer.wrap(packInt(val, MIN_SIZE_LENGTH)));
}
/**
* Get the Date value of this element
- *
+ *
* @return Date of this element
*/
public Date getDate()
@@ -61,7 +61,7 @@ public Date getDate()
* long diff1 = start.getTime(); long diff2 = end.getTime(); long diff3 = Date.UTC(2001, 1, 1, 0, 0, 0) - Date.UTC(1970, 1, 1, 0, 0, 0);
*/
long val = getValue();
- val = val / 1000000000 + UnixEpochDelay;
+ val = val / 1000000 + UnixEpochDelay;
return new Date(val);
}
diff --git a/src/main/java/org/ebml/matroska/MatroskaDocType.java b/src/main/java/org/ebml/matroska/MatroskaDocType.java
index b820120..405e9f0 100644
--- a/src/main/java/org/ebml/matroska/MatroskaDocType.java
+++ b/src/main/java/org/ebml/matroska/MatroskaDocType.java
@@ -22,14 +22,6 @@ public enum MatroskaDocType
DocTypeReadVersion(MatroskaDocTypes.DocTypeReadVersion),
Void(MatroskaDocTypes.Void),
CRC_32(MatroskaDocTypes.CRC_32),
- SignatureSlot(MatroskaDocTypes.SignatureSlot),
- SignatureAlgo(MatroskaDocTypes.SignatureAlgo),
- SignatureHash(MatroskaDocTypes.SignatureHash),
- SignaturePublicKey(MatroskaDocTypes.SignaturePublicKey),
- Signature(MatroskaDocTypes.Signature),
- SignatureElements(MatroskaDocTypes.SignatureElements),
- SignatureElementList(MatroskaDocTypes.SignatureElementList),
- SignedElement(MatroskaDocTypes.SignedElement),
Segment(MatroskaDocTypes.Segment),
SeekHead(MatroskaDocTypes.SeekHead),
Seek(MatroskaDocTypes.Seek),
@@ -92,6 +84,11 @@ public enum MatroskaDocType
FlagEnabled(MatroskaDocTypes.FlagEnabled),
FlagDefault(MatroskaDocTypes.FlagDefault),
FlagForced(MatroskaDocTypes.FlagForced),
+ FlagHearingImpaired(MatroskaDocTypes.FlagHearingImpaired),
+ FlagVisualImpaired(MatroskaDocTypes.FlagVisualImpaired),
+ FlagTextDescriptions(MatroskaDocTypes.FlagTextDescriptions),
+ FlagOriginal(MatroskaDocTypes.FlagOriginal),
+ FlagCommentary(MatroskaDocTypes.FlagCommentary),
FlagLacing(MatroskaDocTypes.FlagLacing),
MinCache(MatroskaDocTypes.MinCache),
MaxCache(MatroskaDocTypes.MaxCache),
@@ -100,6 +97,11 @@ public enum MatroskaDocType
TrackTimecodeScale(MatroskaDocTypes.TrackTimecodeScale),
TrackOffset(MatroskaDocTypes.TrackOffset),
MaxBlockAdditionID(MatroskaDocTypes.MaxBlockAdditionID),
+ BlockAdditionMapping(MatroskaDocTypes.BlockAdditionMapping),
+ BlockAddIDValue(MatroskaDocTypes.BlockAddIDValue),
+ BlockAddIDName(MatroskaDocTypes.BlockAddIDName),
+ BlockAddIDType(MatroskaDocTypes.BlockAddIDType),
+ BlockAddIDExtraData(MatroskaDocTypes.BlockAddIDExtraData),
Name(MatroskaDocTypes.Name),
Language(MatroskaDocTypes.Language),
LanguageIETF(MatroskaDocTypes.LanguageIETF),
@@ -174,6 +176,7 @@ public enum MatroskaDocType
Channels(MatroskaDocTypes.Channels),
ChannelPositions(MatroskaDocTypes.ChannelPositions),
BitDepth(MatroskaDocTypes.BitDepth),
+ Emphasis(MatroskaDocTypes.Emphasis),
TrackOperation(MatroskaDocTypes.TrackOperation),
TrackCombinePlanes(MatroskaDocTypes.TrackCombinePlanes),
TrackPlane(MatroskaDocTypes.TrackPlane),
@@ -234,6 +237,9 @@ public enum MatroskaDocType
EditionFlagHidden(MatroskaDocTypes.EditionFlagHidden),
EditionFlagDefault(MatroskaDocTypes.EditionFlagDefault),
EditionFlagOrdered(MatroskaDocTypes.EditionFlagOrdered),
+ EditionDisplay(MatroskaDocTypes.EditionDisplay),
+ EditionString(MatroskaDocTypes.EditionString),
+ EditionLanguageIETF(MatroskaDocTypes.EditionLanguageIETF),
ChapterAtom(MatroskaDocTypes.ChapterAtom),
ChapterUID(MatroskaDocTypes.ChapterUID),
ChapterStringUID(MatroskaDocTypes.ChapterStringUID),
@@ -242,6 +248,7 @@ public enum MatroskaDocType
ChapterFlagHidden(MatroskaDocTypes.ChapterFlagHidden),
ChapterFlagEnabled(MatroskaDocTypes.ChapterFlagEnabled),
ChapterSegmentUID(MatroskaDocTypes.ChapterSegmentUID),
+ ChapterSkipType(MatroskaDocTypes.ChapterSkipType),
ChapterSegmentEditionUID(MatroskaDocTypes.ChapterSegmentEditionUID),
ChapterPhysicalEquiv(MatroskaDocTypes.ChapterPhysicalEquiv),
ChapterTrack(MatroskaDocTypes.ChapterTrack),
@@ -271,6 +278,7 @@ public enum MatroskaDocType
TagLanguage(MatroskaDocTypes.TagLanguage),
TagLanguageIETF(MatroskaDocTypes.TagLanguageIETF),
TagDefault(MatroskaDocTypes.TagDefault),
+ TagDefaultBogus(MatroskaDocTypes.TagDefaultBogus),
TagString(MatroskaDocTypes.TagString),
TagBinary(MatroskaDocTypes.TagBinary),
UNKNOWN(MatroskaDocTypes.Void); // Not a recognized element type
diff --git a/src/main/java/org/ebml/matroska/MatroskaDocTypes.java b/src/main/java/org/ebml/matroska/MatroskaDocTypes.java
index b911d7a..81515fc 100644
--- a/src/main/java/org/ebml/matroska/MatroskaDocTypes.java
+++ b/src/main/java/org/ebml/matroska/MatroskaDocTypes.java
@@ -25,14 +25,6 @@ public final class MatroskaDocTypes
public static final ProtoType DocTypeReadVersion = new ProtoType<>(UnsignedIntegerElement.class, "DocTypeReadVersion", new byte[] {(byte) 0x42, (byte) 0x85 }, 1);
public static final ProtoType Void = new ProtoType<>(BinaryElement.class, "Void", new byte[] {(byte) 0xEC }, -1);
public static final ProtoType CRC_32 = new ProtoType<>(BinaryElement.class, "CRC_32", new byte[] {(byte) 0xBF }, -1);
- public static final ProtoType SignatureSlot = new ProtoType<>(MasterElement.class, "SignatureSlot", new byte[] {(byte) 0x1B, (byte) 0x53, (byte) 0x86, (byte) 0x67 }, -1);
- public static final ProtoType SignatureAlgo = new ProtoType<>(UnsignedIntegerElement.class, "SignatureAlgo", new byte[] {(byte) 0x7E, (byte) 0x8A }, 1);
- public static final ProtoType SignatureHash = new ProtoType<>(UnsignedIntegerElement.class, "SignatureHash", new byte[] {(byte) 0x7E, (byte) 0x9A }, 1);
- public static final ProtoType SignaturePublicKey = new ProtoType<>(BinaryElement.class, "SignaturePublicKey", new byte[] {(byte) 0x7E, (byte) 0xA5 }, 1);
- public static final ProtoType Signature = new ProtoType<>(BinaryElement.class, "Signature", new byte[] {(byte) 0x7E, (byte) 0xB5 }, 1);
- public static final ProtoType SignatureElements = new ProtoType<>(MasterElement.class, "SignatureElements", new byte[] {(byte) 0x7E, (byte) 0x5B }, 1);
- public static final ProtoType SignatureElementList = new ProtoType<>(MasterElement.class, "SignatureElementList", new byte[] {(byte) 0x7E, (byte) 0x7B }, 2);
- public static final ProtoType SignedElement = new ProtoType<>(BinaryElement.class, "SignedElement", new byte[] {(byte) 0x65, (byte) 0x32 }, 3);
public static final ProtoType Segment = new ProtoType<>(MasterElement.class, "Segment", new byte[] {(byte) 0x18, (byte) 0x53, (byte) 0x80, (byte) 0x67 }, 0);
public static final ProtoType SeekHead = new ProtoType<>(MasterElement.class, "SeekHead", new byte[] {(byte) 0x11, (byte) 0x4D, (byte) 0x9B, (byte) 0x74 }, 1);
public static final ProtoType Seek = new ProtoType<>(MasterElement.class, "Seek", new byte[] {(byte) 0x4D, (byte) 0xBB }, 2);
@@ -95,6 +87,11 @@ public final class MatroskaDocTypes
public static final ProtoType FlagEnabled = new ProtoType<>(UnsignedIntegerElement.class, "FlagEnabled", new byte[] {(byte) 0xB9 }, 3);
public static final ProtoType FlagDefault = new ProtoType<>(UnsignedIntegerElement.class, "FlagDefault", new byte[] {(byte) 0x88 }, 3);
public static final ProtoType FlagForced = new ProtoType<>(UnsignedIntegerElement.class, "FlagForced", new byte[] {(byte) 0x55, (byte) 0xAA }, 3);
+ public static final ProtoType FlagHearingImpaired = new ProtoType<>(UnsignedIntegerElement.class, "FlagHearingImpaired", new byte[] {(byte) 0x55, (byte) 0xAB }, 3);
+ public static final ProtoType FlagVisualImpaired = new ProtoType<>(UnsignedIntegerElement.class, "FlagVisualImpaired", new byte[] {(byte) 0x55, (byte) 0xAC }, 3);
+ public static final ProtoType FlagTextDescriptions = new ProtoType<>(UnsignedIntegerElement.class, "FlagTextDescriptions", new byte[] {(byte) 0x55, (byte) 0xAD }, 3);
+ public static final ProtoType FlagOriginal = new ProtoType<>(UnsignedIntegerElement.class, "FlagOriginal", new byte[] {(byte) 0x55, (byte) 0xAE }, 3);
+ public static final ProtoType FlagCommentary = new ProtoType<>(UnsignedIntegerElement.class, "FlagCommentary", new byte[] {(byte) 0x55, (byte) 0xAF }, 3);
public static final ProtoType FlagLacing = new ProtoType<>(UnsignedIntegerElement.class, "FlagLacing", new byte[] {(byte) 0x9C }, 3);
public static final ProtoType MinCache = new ProtoType<>(UnsignedIntegerElement.class, "MinCache", new byte[] {(byte) 0x6D, (byte) 0xE7 }, 3);
public static final ProtoType MaxCache = new ProtoType<>(UnsignedIntegerElement.class, "MaxCache", new byte[] {(byte) 0x6D, (byte) 0xF8 }, 3);
@@ -103,6 +100,11 @@ public final class MatroskaDocTypes
public static final ProtoType TrackTimecodeScale = new ProtoType<>(FloatElement.class, "TrackTimecodeScale", new byte[] {(byte) 0x23, (byte) 0x31, (byte) 0x4F }, 3);
public static final ProtoType TrackOffset = new ProtoType<>(SignedIntegerElement.class, "TrackOffset", new byte[] {(byte) 0x53, (byte) 0x7F }, 3);
public static final ProtoType MaxBlockAdditionID = new ProtoType<>(UnsignedIntegerElement.class, "MaxBlockAdditionID", new byte[] {(byte) 0x55, (byte) 0xEE }, 3);
+ public static final ProtoType BlockAdditionMapping = new ProtoType<>(UnsignedIntegerElement.class, "BlockAdditionMapping", new byte[] {(byte) 0x41, (byte) 0xE4 }, 3);
+ public static final ProtoType BlockAddIDValue = new ProtoType<>(UnsignedIntegerElement.class, "BlockAddIDValue", new byte[] {(byte) 0x41, (byte) 0xF0 }, 3);
+ public static final ProtoType BlockAddIDName = new ProtoType<>(UnsignedIntegerElement.class, "BlockAddIDName", new byte[] {(byte) 0x41, (byte) 0xA4 }, 3);
+ public static final ProtoType BlockAddIDType = new ProtoType<>(UnsignedIntegerElement.class, "BlockAddIDType", new byte[] {(byte) 0x41, (byte) 0xE7 }, 3);
+ public static final ProtoType BlockAddIDExtraData = new ProtoType<>(UnsignedIntegerElement.class, "BlockAddIDExtraData", new byte[] {(byte) 0x41, (byte) 0xED }, 3);
public static final ProtoType Name = new ProtoType<>(UTF8StringElement.class, "Name", new byte[] {(byte) 0x53, (byte) 0x6E }, 3);
public static final ProtoType Language = new ProtoType<>(StringElement.class, "Language", new byte[] {(byte) 0x22, (byte) 0xB5, (byte) 0x9C }, 3);
public static final ProtoType LanguageIETF = new ProtoType<>(StringElement.class, "LanguageIETF", new byte[] {(byte) 0x22, (byte) 0xB5, (byte) 0x9D }, 3);
@@ -177,6 +179,7 @@ public final class MatroskaDocTypes
public static final ProtoType Channels = new ProtoType<>(UnsignedIntegerElement.class, "Channels", new byte[] {(byte) 0x9F }, 4);
public static final ProtoType ChannelPositions = new ProtoType<>(BinaryElement.class, "ChannelPositions", new byte[] {(byte) 0x7D, (byte) 0x7B }, 4);
public static final ProtoType BitDepth = new ProtoType<>(UnsignedIntegerElement.class, "BitDepth", new byte[] {(byte) 0x62, (byte) 0x64 }, 4);
+ public static final ProtoType Emphasis = new ProtoType<>(UnsignedIntegerElement.class, "Emphasis", new byte[] {(byte) 0x52, (byte) 0xF1 }, 4);
public static final ProtoType TrackOperation = new ProtoType<>(MasterElement.class, "TrackOperation", new byte[] {(byte) 0xE2 }, 3);
public static final ProtoType TrackCombinePlanes = new ProtoType<>(MasterElement.class, "TrackCombinePlanes", new byte[] {(byte) 0xE3 }, 4);
public static final ProtoType TrackPlane = new ProtoType<>(MasterElement.class, "TrackPlane", new byte[] {(byte) 0xE4 }, 5);
@@ -237,6 +240,9 @@ public final class MatroskaDocTypes
public static final ProtoType EditionFlagHidden = new ProtoType<>(UnsignedIntegerElement.class, "EditionFlagHidden", new byte[] {(byte) 0x45, (byte) 0xBD }, 3);
public static final ProtoType EditionFlagDefault = new ProtoType<>(UnsignedIntegerElement.class, "EditionFlagDefault", new byte[] {(byte) 0x45, (byte) 0xDB }, 3);
public static final ProtoType EditionFlagOrdered = new ProtoType<>(UnsignedIntegerElement.class, "EditionFlagOrdered", new byte[] {(byte) 0x45, (byte) 0xDD }, 3);
+ public static final ProtoType EditionDisplay = new ProtoType<>(UnsignedIntegerElement.class, "EditionDisplay", new byte[] {(byte) 0x45, (byte) 0x20 }, 3);
+ public static final ProtoType EditionString = new ProtoType<>(UnsignedIntegerElement.class, "EditionString", new byte[] {(byte) 0x45, (byte) 0x21 }, 3);
+ public static final ProtoType EditionLanguageIETF = new ProtoType<>(UnsignedIntegerElement.class, "EditionLanguageIETF", new byte[] {(byte) 0x45, (byte) 0xE4 }, 3);
public static final ProtoType ChapterAtom = new ProtoType<>(MasterElement.class, "ChapterAtom", new byte[] {(byte) 0xB6 }, 3);
public static final ProtoType ChapterUID = new ProtoType<>(UnsignedIntegerElement.class, "ChapterUID", new byte[] {(byte) 0x73, (byte) 0xC4 }, 4);
public static final ProtoType ChapterStringUID = new ProtoType<>(UTF8StringElement.class, "ChapterStringUID", new byte[] {(byte) 0x56, (byte) 0x54 }, 4);
@@ -245,6 +251,7 @@ public final class MatroskaDocTypes
public static final ProtoType ChapterFlagHidden = new ProtoType<>(UnsignedIntegerElement.class, "ChapterFlagHidden", new byte[] {(byte) 0x98 }, 4);
public static final ProtoType ChapterFlagEnabled = new ProtoType<>(UnsignedIntegerElement.class, "ChapterFlagEnabled", new byte[] {(byte) 0x45, (byte) 0x98 }, 4);
public static final ProtoType ChapterSegmentUID = new ProtoType<>(BinaryElement.class, "ChapterSegmentUID", new byte[] {(byte) 0x6E, (byte) 0x67 }, 4);
+ public static final ProtoType ChapterSkipType = new ProtoType<>(BinaryElement.class, "ChapterSkipType", new byte[] {(byte) 0x45, (byte) 0x88 }, 4);
public static final ProtoType ChapterSegmentEditionUID = new ProtoType<>(UnsignedIntegerElement.class, "ChapterSegmentEditionUID", new byte[] {(byte) 0x6E, (byte) 0xBC }, 4);
public static final ProtoType ChapterPhysicalEquiv = new ProtoType<>(UnsignedIntegerElement.class, "ChapterPhysicalEquiv", new byte[] {(byte) 0x63, (byte) 0xC3 }, 4);
public static final ProtoType ChapterTrack = new ProtoType<>(MasterElement.class, "ChapterTrack", new byte[] {(byte) 0x8F }, 4);
@@ -274,6 +281,7 @@ public final class MatroskaDocTypes
public static final ProtoType TagLanguage = new ProtoType<>(StringElement.class, "TagLanguage", new byte[] {(byte) 0x44, (byte) 0x7A }, 4);
public static final ProtoType TagLanguageIETF = new ProtoType<>(StringElement.class, "TagLanguageIETF", new byte[] {(byte) 0x44, (byte) 0x7B }, 4);
public static final ProtoType TagDefault = new ProtoType<>(UnsignedIntegerElement.class, "TagDefault", new byte[] {(byte) 0x44, (byte) 0x84 }, 4);
+ public static final ProtoType TagDefaultBogus = new ProtoType<>(UnsignedIntegerElement.class, "TagDefaultBogus", new byte[] {(byte) 0x44, (byte) 0xB4 }, 4);
public static final ProtoType TagString = new ProtoType<>(UTF8StringElement.class, "TagString", new byte[] {(byte) 0x44, (byte) 0x87 }, 4);
public static final ProtoType TagBinary = new ProtoType<>(BinaryElement.class, "TagBinary", new byte[] {(byte) 0x44, (byte) 0x85 }, 4);
diff --git a/src/main/java/org/ebml/matroska/MatroskaFile.java b/src/main/java/org/ebml/matroska/MatroskaFile.java
index 4f8afdc..756e78e 100644
--- a/src/main/java/org/ebml/matroska/MatroskaFile.java
+++ b/src/main/java/org/ebml/matroska/MatroskaFile.java
@@ -448,10 +448,13 @@ private synchronized void fillFrameQueue()
{
parseNextCluster(level1);
clusterReadIndex++;
+ level1.skipData(ioDS);
}
- level1.skipData(ioDS);
- level1 = ((MasterElement) level0).readNextChild(reader);
+ if (frameQueue.isEmpty())
+ {
+ level1 = ((MasterElement) level0).readNextChild(reader);
+ }
}
}
diff --git a/src/main/java/org/ebml/matroska/MatroskaFileCues.java b/src/main/java/org/ebml/matroska/MatroskaFileCues.java
index e145ef2..3fd7da9 100644
--- a/src/main/java/org/ebml/matroska/MatroskaFileCues.java
+++ b/src/main/java/org/ebml/matroska/MatroskaFileCues.java
@@ -45,13 +45,13 @@ private MasterElement createCueTrackPositions(long positionInFile, int trackNumb
cueTrackPositions.addChildElement(cueTrack);
UnsignedIntegerElement cueClusterPosition = MatroskaDocTypes.CueClusterPosition.getInstance();
- cueClusterPosition.setValue(getPositionRelativeToSegmentEbmlElement(positionInFile));
+ cueClusterPosition.setValue(positionInFile - endOfEbmlHeaderBytePosition);
cueTrackPositions.addChildElement(cueClusterPosition);
return cueTrackPositions;
}
- public Element write(DataWriter ioDW, MatroskaFileMetaSeek metaSeek)
+ public long write(DataWriter ioDW, MatroskaFileMetaSeek metaSeek)
{
long currentBytePositionInFile = ioDW.getFilePointer();
LOG.debug("Writing matroska cues at file byte position [{}]", currentBytePositionInFile);
@@ -59,11 +59,6 @@ public Element write(DataWriter ioDW, MatroskaFileMetaSeek metaSeek)
LOG.debug("Done writing matroska cues, number of bytes was [{}]", numberOfBytesInCueData);
metaSeek.addIndexedElement(cues, currentBytePositionInFile);
- return cues;
- }
-
- private long getPositionRelativeToSegmentEbmlElement(long currentBytePositionInFile)
- {
- return currentBytePositionInFile - endOfEbmlHeaderBytePosition;
+ return numberOfBytesInCueData;
}
}
diff --git a/src/main/java/org/ebml/matroska/MatroskaFileMetaSeek.java b/src/main/java/org/ebml/matroska/MatroskaFileMetaSeek.java
index 202aab0..cb7bc99 100644
--- a/src/main/java/org/ebml/matroska/MatroskaFileMetaSeek.java
+++ b/src/main/java/org/ebml/matroska/MatroskaFileMetaSeek.java
@@ -53,14 +53,16 @@ public long write(final DataWriter ioDW)
*
* @param ioDW the data stream containing this object
*/
- public void update(final DataWriter ioDW)
+ public long update(final DataWriter ioDW)
{
assert ioDW.isSeekable();
final long pos = ioDW.getFilePointer();
ioDW.seek(myPosition);
- write(ioDW);
+ long len = write(ioDW);
ioDW.seek(pos);
LOG.debug("Updated metaseek section.");
+
+ return len;
}
/**
diff --git a/src/main/java/org/ebml/matroska/MatroskaFileTagEntry.java b/src/main/java/org/ebml/matroska/MatroskaFileTagEntry.java
index dae48b7..4f9a920 100644
--- a/src/main/java/org/ebml/matroska/MatroskaFileTagEntry.java
+++ b/src/main/java/org/ebml/matroska/MatroskaFileTagEntry.java
@@ -33,6 +33,8 @@ public class MatroskaFileTagEntry
public ArrayList trackUID = new ArrayList<>();
public ArrayList chapterUID = new ArrayList<>();
public ArrayList attachmentUID = new ArrayList<>();
+ public ArrayList editionUID = new ArrayList<>();
+
public List simpleTags = new ArrayList<>();
public void addSimpleTag(final MatroskaFileSimpleTag simpleTag)
@@ -45,6 +47,26 @@ Element toElement()
MasterElement tagEntryElem = MatroskaDocTypes.Tag.getInstance();
MasterElement targetsEntryElem = MatroskaDocTypes.Targets.getInstance();
+ trackUID.forEach((uid) -> {
+ UnsignedIntegerElement trackUIDElem = MatroskaDocTypes.TagTrackUID.getInstance();
+ trackUIDElem.setValue(uid);
+ targetsEntryElem.addChildElement(trackUIDElem);
+ });
+ chapterUID.forEach((uid) -> {
+ UnsignedIntegerElement chapterUIDElem = MatroskaDocTypes.TagChapterUID.getInstance();
+ chapterUIDElem.setValue(uid);
+ targetsEntryElem.addChildElement(chapterUIDElem);
+ });
+ attachmentUID.forEach((uid) -> {
+ UnsignedIntegerElement attachmentUIDElem = MatroskaDocTypes.TagAttachmentUID.getInstance();
+ attachmentUIDElem.setValue(uid);
+ targetsEntryElem.addChildElement(attachmentUIDElem);
+ });
+ editionUID.forEach((uid) -> {
+ UnsignedIntegerElement editionUIDElem = MatroskaDocTypes.TagEditionUID.getInstance();
+ editionUIDElem.setValue(uid);
+ targetsEntryElem.addChildElement(editionUIDElem);
+ });
tagEntryElem.addChildElement(targetsEntryElem);
for (MatroskaFileSimpleTag simpleTag : simpleTags)
diff --git a/src/main/java/org/ebml/matroska/MatroskaFileTags.java b/src/main/java/org/ebml/matroska/MatroskaFileTags.java
index 3fa7353..4d8c3ad 100644
--- a/src/main/java/org/ebml/matroska/MatroskaFileTags.java
+++ b/src/main/java/org/ebml/matroska/MatroskaFileTags.java
@@ -39,12 +39,13 @@ public long writeTags(final DataWriter ioDW)
return len;
}
- public void update(final DataWriter ioDW)
+ public long update(final DataWriter ioDW)
{
LOG.info("Updating tags list!");
final long start = ioDW.getFilePointer();
ioDW.seek(myPosition);
- writeTags(ioDW);
+ long len = writeTags(ioDW);
ioDW.seek(start);
+ return len;
}
}
diff --git a/src/main/java/org/ebml/matroska/MatroskaFileTrack.java b/src/main/java/org/ebml/matroska/MatroskaFileTrack.java
index 86c805f..428cf76 100644
--- a/src/main/java/org/ebml/matroska/MatroskaFileTrack.java
+++ b/src/main/java/org/ebml/matroska/MatroskaFileTrack.java
@@ -60,7 +60,7 @@ public class MatroskaFileTrack
private String language = "eng";
private String codecID;
private ByteBuffer codecPrivate;
- private long defaultDuration;
+ private Long defaultDuration;
private boolean codecDecodeAll = true;
private int seekPreroll = 0;
@@ -171,10 +171,10 @@ public void setColourSpace(ByteBuffer colourSpace)
public static class MatroskaAudioTrack
{
- private float samplingFrequency;
- private float outputSamplingFrequency;
- private short channels;
- private byte bitDepth;
+ private float samplingFrequency = 8000.0f;
+ private Float outputSamplingFrequency;
+ private short channels = 1;
+ private Byte bitDepth;
public float getSamplingFrequency()
{
@@ -186,7 +186,7 @@ public void setSamplingFrequency(final float samplingFrequency)
this.samplingFrequency = samplingFrequency;
}
- public float getOutputSamplingFrequency()
+ public Float getOutputSamplingFrequency()
{
return outputSamplingFrequency;
}
@@ -206,7 +206,7 @@ public void setChannels(final short channels)
this.channels = channels;
}
- public byte getBitDepth()
+ public Byte getBitDepth()
{
return bitDepth;
}
@@ -247,7 +247,10 @@ public String toString()
s += "\t\t" + "TrackNo: " + getTrackNo() + "\n";
s += "\t\t" + "TrackUID: " + getTrackUID() + "\n";
s += "\t\t" + "TrackType: " + getTrackType().name() + "\n";
- s += "\t\t" + "DefaultDuration: " + getDefaultDuration() + "\n";
+ if (this.defaultDuration != null)
+ {
+ s += "\t\t" + "DefaultDuration: " + this.defaultDuration + "\n";
+ }
s += "\t\t" + "Name: " + getName() + "\n";
s += "\t\t" + "Language: " + getLanguage() + "\n";
s += "\t\t" + "CodecID: " + getCodecID() + "\n";
@@ -481,18 +484,21 @@ Element toElement()
trackEntryElem.addChildElement(trackCodecPrivateElem);
}
- final UnsignedIntegerElement trackDefaultDurationElem = MatroskaDocTypes.DefaultDuration.getInstance();
- trackDefaultDurationElem.setValue(this.getDefaultDuration());
-
final UnsignedIntegerElement trackCodecDecodeAllElem = MatroskaDocTypes.CodecDecodeAll.getInstance();
trackCodecDecodeAllElem.setValue(this.codecDecodeAll ? 1 : 0);
- // final UnsignedIntegerElement trackSeekPrerollElem = (UnsignedIntegerElement) doc.createElement(MatroskaDocTypes.TrackSeekPreroll);
- // trackSeekPrerollElem.setValue(this.seekPreroll);
+ final UnsignedIntegerElement trackSeekPrerollElem = MatroskaDocTypes.SeekPreRoll.getInstance();
+ trackSeekPrerollElem.setValue(this.seekPreroll);
+
+ if (this.defaultDuration != null)
+ {
+ UnsignedIntegerElement trackDefaultDurationElem = MatroskaDocTypes.DefaultDuration.getInstance();
+ trackDefaultDurationElem.setValue(this.getDefaultDuration());
+ trackEntryElem.addChildElement(trackDefaultDurationElem);
+ }
- trackEntryElem.addChildElement(trackDefaultDurationElem);
trackEntryElem.addChildElement(trackCodecDecodeAllElem);
- // trackEntryElem.addChildElement(trackSeekPrerollElem);
+ trackEntryElem.addChildElement(trackSeekPrerollElem);
if (!overlayUids.isEmpty())
{
@@ -546,19 +552,34 @@ else if (this.getTrackType() == TrackType.AUDIO)
final UnsignedIntegerElement trackAudioChannelsElem = MatroskaDocTypes.Channels.getInstance();
trackAudioChannelsElem.setValue(this.audio.getChannels());
- final UnsignedIntegerElement trackAudioBitDepthElem = MatroskaDocTypes.BitDepth.getInstance();
- trackAudioBitDepthElem.setValue(this.audio.getBitDepth());
+ UnsignedIntegerElement trackAudioBitDepthElem = null;
+ if (this.audio.getBitDepth() != null)
+ {
+ trackAudioBitDepthElem = MatroskaDocTypes.BitDepth.getInstance();
+ trackAudioBitDepthElem.setValue(this.audio.getBitDepth());
+ }
final FloatElement trackAudioSamplingRateElem = MatroskaDocTypes.SamplingFrequency.getInstance();
trackAudioSamplingRateElem.setValue(this.audio.getSamplingFrequency());
- final FloatElement trackAudioOutputSamplingFrequencyElem = MatroskaDocTypes.OutputSamplingFrequency.getInstance();
- trackAudioOutputSamplingFrequencyElem.setValue(this.audio.getOutputSamplingFrequency());
+ FloatElement trackAudioOutputSamplingFrequencyElem = null;
+ if (this.audio.getOutputSamplingFrequency() != null)
+ {
+ trackAudioOutputSamplingFrequencyElem = MatroskaDocTypes.OutputSamplingFrequency.getInstance();
+ trackAudioOutputSamplingFrequencyElem.setValue(this.audio.getOutputSamplingFrequency());
+ }
+
trackAudioElem.addChildElement(trackAudioChannelsElem);
- trackAudioElem.addChildElement(trackAudioBitDepthElem);
+ if (trackAudioBitDepthElem != null)
+ {
+ trackAudioElem.addChildElement(trackAudioBitDepthElem);
+ }
trackAudioElem.addChildElement(trackAudioSamplingRateElem);
- trackAudioElem.addChildElement(trackAudioOutputSamplingFrequencyElem);
+ if (trackAudioOutputSamplingFrequencyElem != null)
+ {
+ trackAudioElem.addChildElement(trackAudioOutputSamplingFrequencyElem);
+ }
trackEntryElem.addChildElement(trackAudioElem);
}
diff --git a/src/main/java/org/ebml/matroska/MatroskaFileTracks.java b/src/main/java/org/ebml/matroska/MatroskaFileTracks.java
index 274c7bc..bb19db2 100644
--- a/src/main/java/org/ebml/matroska/MatroskaFileTracks.java
+++ b/src/main/java/org/ebml/matroska/MatroskaFileTracks.java
@@ -36,12 +36,13 @@ public long writeTracks(final DataWriter ioDW)
return BLOCK_SIZE;
}
- public void update(final DataWriter ioDW)
+ public long update(final DataWriter ioDW)
{
LOG.info("Updating tracks list!");
final long start = ioDW.getFilePointer();
ioDW.seek(myPosition);
- writeTracks(ioDW);
+ long len = writeTracks(ioDW);
ioDW.seek(start);
+ return len;
}
}
diff --git a/src/main/java/org/ebml/matroska/MatroskaFileWriter.java b/src/main/java/org/ebml/matroska/MatroskaFileWriter.java
index 5f249e7..80c8a20 100644
--- a/src/main/java/org/ebml/matroska/MatroskaFileWriter.java
+++ b/src/main/java/org/ebml/matroska/MatroskaFileWriter.java
@@ -22,6 +22,7 @@
import java.io.Closeable;
import java.util.HashSet;
import java.util.Set;
+import java.util.concurrent.*;
import org.ebml.MasterElement;
import org.ebml.StringElement;
@@ -39,6 +40,7 @@ public class MatroskaFileWriter implements Closeable
protected DataWriter ioDW;
+ private MatroskaSegment segmentElem = null;
private MatroskaFileMetaSeek metaSeek;
private MatroskaFileCues cueData;
private MatroskaCluster cluster;
@@ -50,6 +52,11 @@ public class MatroskaFileWriter implements Closeable
private boolean onlyAudioTracks;
+ private long clusterLen = 0;
+
+ private long minSegmentTimecode = Long.MAX_VALUE;
+ private long maxSegmentTimecode = Long.MIN_VALUE;
+
/**
* @param outputDataWriter DataWriter to write out to.
*/
@@ -130,31 +137,11 @@ void writeEBMLHeader()
void writeSegmentHeader()
{
- final MatroskaSegment segmentElem = new MatroskaSegment();
+ this.segmentElem = new MatroskaSegment();
segmentElem.setUnknownSize(true);
segmentElem.writeHeaderData(ioDW);
}
- void writeSegmentInfo()
- {
- segmentInfoElem.update(ioDW);
- }
-
- void writeTracks()
- {
- tracks.update(ioDW);
- }
-
- void writeTags()
- {
- tags.update(ioDW);
- }
-
- public long getTimecodeScale()
- {
- return segmentInfoElem.getTimecodeScale();
- }
-
/**
* Sets the time scale used in this file. This is the number of nanoseconds represented by the timecode unit in frames. Defaults to 1,000,000.
*
@@ -254,25 +241,28 @@ public void addFrame(final MatroskaFileFrame frame)
|| (frame.isKeyFrame() && videoTrackNumbers.contains(frame.getTrackNo())))
{
flush();
-
- if (ioDW.isSeekable())
- {
- final long clusterPos = ioDW.getFilePointer();
- cueData.addCue(clusterPos, frame.getTimecode(), frame.getTrackNo());
- }
}
+
+ boolean addCue = !cluster.getTracks().contains(frame.getTrackNo());
cluster.addFrame(frame);
+ minSegmentTimecode = Math.min(minSegmentTimecode, frame.getTimecode());
+ maxSegmentTimecode = Math.max(maxSegmentTimecode, frame.getTimecode());
+
+ if (ioDW.isSeekable() && addCue)
+ {
+ cueData.addCue(ioDW.getFilePointer(), frame.getTimecode(), frame.getTrackNo());
+ }
}
/**
* Flushes pending content to disk and starts a new cluster. This is typically not necessary to call manually.
*/
- public void flush()
+ private void flush()
{
initialize();
LOG.debug("Cluster flushing, timecode {}", cluster.getClusterTimecode());
- cluster.flush(ioDW);
+ clusterLen += cluster.flush(ioDW);
}
/**
@@ -285,11 +275,21 @@ public void close()
if (ioDW.isSeekable())
{
- cueData.write(ioDW, metaSeek);
- metaSeek.update(ioDW);
- segmentInfoElem.update(ioDW);
- tracks.update(ioDW);
- tags.update(ioDW);
+ long segmentLen = 0;
+
+ segmentLen += cueData.write(ioDW, metaSeek);
+ segmentLen += metaSeek.update(ioDW);
+
+ segmentInfoElem.setDuration(maxSegmentTimecode - minSegmentTimecode);
+ segmentLen += segmentInfoElem.update(ioDW);
+
+ segmentLen += tracks.update(ioDW);
+ segmentLen += tags.update(ioDW);
+ segmentLen += clusterLen;
+
+ segmentElem.setUnknownSize(false);
+ segmentElem.setSize(segmentLen);
+ segmentElem.update(ioDW);
}
}
}
diff --git a/src/main/java/org/ebml/matroska/MatroskaSegment.java b/src/main/java/org/ebml/matroska/MatroskaSegment.java
index 4ba1094..2ffafc0 100644
--- a/src/main/java/org/ebml/matroska/MatroskaSegment.java
+++ b/src/main/java/org/ebml/matroska/MatroskaSegment.java
@@ -30,6 +30,7 @@
*/
public class MatroskaSegment extends MasterElement
{
+ private long myPosition;
protected boolean bUnknownSize = false;
public MatroskaSegment()
@@ -44,6 +45,7 @@ public MatroskaSegment()
@Override
public long writeHeaderData(final DataWriter writer)
{
+ myPosition = writer.getFilePointer();
long len = 0;
final ByteBuffer type = getType();
@@ -62,7 +64,7 @@ public long writeHeaderData(final DataWriter writer)
}
else
{
- size = Element.makeEbmlCodedSize(getSize());
+ size = Element.makeEbmlCodedSize(getSize(), 5);
}
len += size.length;
@@ -71,6 +73,15 @@ public long writeHeaderData(final DataWriter writer)
return len;
}
+ public void update(final DataWriter ioDW)
+ {
+ LOG.debug("Updating segment header size");
+ final long startingPos = ioDW.getFilePointer();
+ ioDW.seek(myPosition);
+ writeHeaderData(ioDW);
+ ioDW.seek(startingPos);
+ }
+
/**
* Setter for Unknown Size flag. This is a special case for ebml. The size value is filled with 1's.
*/
diff --git a/src/main/java/org/ebml/matroska/MatroskaSegmentInfo.java b/src/main/java/org/ebml/matroska/MatroskaSegmentInfo.java
index 0470354..1e90517 100644
--- a/src/main/java/org/ebml/matroska/MatroskaSegmentInfo.java
+++ b/src/main/java/org/ebml/matroska/MatroskaSegmentInfo.java
@@ -54,17 +54,20 @@ public long writeElement(final DataWriter ioDW)
final long len = segmentInfoElem.writeElement(ioDW);
final VoidElement spacer = new VoidElement(BLOCK_SIZE - len);
- spacer.writeElement(ioDW);
- return 1;
+ long voidLen = spacer.writeElement(ioDW);
+
+ assert len + voidLen == BLOCK_SIZE;
+ return BLOCK_SIZE;
}
- public void update(final DataWriter ioDW)
+ public long update(final DataWriter ioDW)
{
LOG.debug("Updating segment info header");
final long startingPos = ioDW.getFilePointer();
ioDW.seek(myPosition);
- writeElement(ioDW);
+ long len = writeElement(ioDW);
ioDW.seek(startingPos);
+ return len;
}
public double getDuration()
From 5cec011c4df36ea32981dfc488e99dc91d0038eb Mon Sep 17 00:00:00 2001
From: bgrozev
Date: Wed, 18 Dec 2024 09:33:12 -0600
Subject: [PATCH 42/42] Fix frame timecode overflow (#5)
* Add "target" to gitignore.
* fix: Fix case where frames might be added out of order
For example, if an entire track is added starting at timecode 0,
and then another track is added starting against at timecode 0.
---
.gitignore | 1 +
.../java/org/ebml/matroska/MatroskaCluster.java | 14 ++++++++++----
.../java/org/ebml/matroska/MatroskaFileWriter.java | 2 +-
3 files changed, 12 insertions(+), 5 deletions(-)
diff --git a/.gitignore b/.gitignore
index 265f4a7..a239578 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,3 +4,4 @@ build/
.classpath
.settings/
.project
+target
diff --git a/src/main/java/org/ebml/matroska/MatroskaCluster.java b/src/main/java/org/ebml/matroska/MatroskaCluster.java
index 49bfbba..4bc41e5 100644
--- a/src/main/java/org/ebml/matroska/MatroskaCluster.java
+++ b/src/main/java/org/ebml/matroska/MatroskaCluster.java
@@ -44,7 +44,7 @@ class MatroskaCluster
private final List sliencedTracks = new ArrayList<>();
private long clusterTimecode = Long.MAX_VALUE;
- private long lastTimecode = 0;
+ private long maxFrameTimecode = 0;
private long durationLimit = 500;
public MatroskaCluster()
@@ -69,14 +69,19 @@ public void addFrame(final MatroskaFileFrame frame)
{
clusterTimecode = frame.getTimecode();
}
- lastTimecode = frame.getTimecode();
+ maxFrameTimecode = Math.max(maxFrameTimecode, frame.getTimecode());
frames.add(frame);
tracks.add(frame.getTrackNo());
}
- public boolean isFlushNeeded()
+ public boolean isFlushNeeded(long nextFrameTimecode)
{
- return (lastTimecode - clusterTimecode) > durationLimit;
+ return
+ // The next frame will exceed the duration limit
+ (nextFrameTimecode - clusterTimecode) > durationLimit ||
+ // The next frame is older, and would lower the cluster timecode enough to overflow the frame timecode
+ // for one of the existing frames.
+ maxFrameTimecode - nextFrameTimecode > Short.MAX_VALUE;
}
public long flush(final DataWriter ioDW)
@@ -136,6 +141,7 @@ public long flush(final DataWriter ioDW)
frames.clear();
tracks.clear();
clusterTimecode = Long.MAX_VALUE;
+ maxFrameTimecode = Long.MIN_VALUE;
}
}
diff --git a/src/main/java/org/ebml/matroska/MatroskaFileWriter.java b/src/main/java/org/ebml/matroska/MatroskaFileWriter.java
index 80c8a20..3087eb0 100644
--- a/src/main/java/org/ebml/matroska/MatroskaFileWriter.java
+++ b/src/main/java/org/ebml/matroska/MatroskaFileWriter.java
@@ -237,7 +237,7 @@ public void unsilenceTrack(final long trackNumber)
public void addFrame(final MatroskaFileFrame frame)
{
initialize();
- if ((onlyAudioTracks && cluster.isFlushNeeded())
+ if ((onlyAudioTracks && cluster.isFlushNeeded(frame.getTimecode()))
|| (frame.isKeyFrame() && videoTrackNumbers.contains(frame.getTrackNo())))
{
flush();