Commit 8c98c588 by Oliver Woodman

Add support for fixed-size lacing in Matroska streams.

parent 4c4782c7
...@@ -52,9 +52,10 @@ public interface Extractor { ...@@ -52,9 +52,10 @@ public interface Extractor {
/** /**
* Extracts data read from a provided {@link ExtractorInput}. * Extracts data read from a provided {@link ExtractorInput}.
* <p> * <p>
* Each read will extract at most one sample from the stream before returning. * A single call to this method will block until some progress has been made, but will not block
* for longer than this. Hence each call will consume only a small amount of input data.
* <p> * <p>
* In the common case, {@link #RESULT_CONTINUE} is returned to indicate that * In the common case, {@link #RESULT_CONTINUE} is returned to indicate that the
* {@link ExtractorInput} passed to the next read is required to provide data continuing from the * {@link ExtractorInput} passed to the next read is required to provide data continuing from the
* position in the stream reached by the returning call. If the extractor requires data to be * position in the stream reached by the returning call. If the extractor requires data to be
* provided from a different position, then that position is set in {@code seekPosition} and * provided from a different position, then that position is set in {@code seekPosition} and
......
...@@ -47,6 +47,14 @@ import java.util.List; ...@@ -47,6 +47,14 @@ import java.util.List;
} }
public static byte[] createByteArray(int... intArray) {
byte[] byteArray = new byte[intArray.length];
for (int i = 0; i < byteArray.length; i++) {
byteArray[i] = (byte) intArray[i];
}
return byteArray;
}
public static byte[] joinByteArrays(byte[]... byteArrays) { public static byte[] joinByteArrays(byte[]... byteArrays) {
int length = 0; int length = 0;
for (byte[] byteArray : byteArrays) { for (byte[] byteArray : byteArrays) {
...@@ -94,16 +102,28 @@ import java.util.List; ...@@ -94,16 +102,28 @@ import java.util.List;
return this; return this;
} }
public StreamBuilder addH264Track(int width, int height, byte[] codecPrivate) {
trackEntries.add(createVideoTrackEntry("V_MPEG4/ISO/AVC", width, height, null, codecPrivate));
return this;
}
public StreamBuilder addOpusTrack(int channelCount, int sampleRate, int codecDelay, public StreamBuilder addOpusTrack(int channelCount, int sampleRate, int codecDelay,
int seekPreRoll, byte[] codecPrivate) { int seekPreRoll, byte[] codecPrivate) {
trackEntries.add(createAudioTrackEntry("A_OPUS", channelCount, sampleRate, codecPrivate, trackEntries.add(createAudioTrackEntry("A_OPUS", channelCount, sampleRate, codecPrivate,
codecDelay, seekPreRoll)); codecDelay, seekPreRoll, NO_VALUE));
return this;
}
public StreamBuilder addOpusTrack(int channelCount, int sampleRate, int codecDelay,
int seekPreRoll, byte[] codecPrivate, int defaultDurationNs) {
trackEntries.add(createAudioTrackEntry("A_OPUS", channelCount, sampleRate, codecPrivate,
codecDelay, seekPreRoll, defaultDurationNs));
return this; return this;
} }
public StreamBuilder addVorbisTrack(int channelCount, int sampleRate, byte[] codecPrivate) { public StreamBuilder addVorbisTrack(int channelCount, int sampleRate, byte[] codecPrivate) {
trackEntries.add(createAudioTrackEntry("A_VORBIS", channelCount, sampleRate, codecPrivate, trackEntries.add(createAudioTrackEntry("A_VORBIS", channelCount, sampleRate, codecPrivate,
NO_VALUE, NO_VALUE)); NO_VALUE, NO_VALUE, NO_VALUE));
return this; return this;
} }
...@@ -120,7 +140,7 @@ import java.util.List; ...@@ -120,7 +140,7 @@ import java.util.List;
byte[] data) { byte[] data) {
byte flags = (byte) ((keyframe ? 0x80 : 0x00) | (invisible ? 0x08 : 0x00)); byte flags = (byte) ((keyframe ? 0x80 : 0x00) | (invisible ? 0x08 : 0x00));
EbmlElement simpleBlockElement = createSimpleBlock(trackNumber, blockTimecode, flags, EbmlElement simpleBlockElement = createSimpleBlock(trackNumber, blockTimecode, flags,
true, validSignalByte, data); true, validSignalByte, 1, data);
mediaSegments.add(createCluster(clusterTimecode, simpleBlockElement)); mediaSegments.add(createCluster(clusterTimecode, simpleBlockElement));
return this; return this;
} }
...@@ -130,7 +150,15 @@ import java.util.List; ...@@ -130,7 +150,15 @@ import java.util.List;
int blockTimecode, boolean keyframe, boolean invisible, byte[] data) { int blockTimecode, boolean keyframe, boolean invisible, byte[] data) {
byte flags = (byte) ((keyframe ? 0x80 : 0x00) | (invisible ? 0x08 : 0x00)); byte flags = (byte) ((keyframe ? 0x80 : 0x00) | (invisible ? 0x08 : 0x00));
EbmlElement simpleBlockElement = createSimpleBlock(trackNumber, blockTimecode, flags, EbmlElement simpleBlockElement = createSimpleBlock(trackNumber, blockTimecode, flags,
false, true, data); false, true, 1, data);
mediaSegments.add(createCluster(clusterTimecode, simpleBlockElement));
return this;
}
public StreamBuilder addSimpleBlockMediaWithFixedSizeLacing(int trackNumber, int clusterTimecode,
int blockTimecode, int lacingFrameCount, byte[] data) {
EbmlElement simpleBlockElement = createSimpleBlock(trackNumber, blockTimecode,
0x80 /* flags = keyframe */, false, true, lacingFrameCount, data);
mediaSegments.add(createCluster(clusterTimecode, simpleBlockElement)); mediaSegments.add(createCluster(clusterTimecode, simpleBlockElement));
return this; return this;
} }
...@@ -248,7 +276,7 @@ import java.util.List; ...@@ -248,7 +276,7 @@ import java.util.List;
} }
private static EbmlElement createAudioTrackEntry(String codecId, int channelCount, int sampleRate, private static EbmlElement createAudioTrackEntry(String codecId, int channelCount, int sampleRate,
byte[] codecPrivate, int codecDelay, int seekPreRoll) { byte[] codecPrivate, int codecDelay, int seekPreRoll, int defaultDurationNs) {
byte channelCountByte = (byte) (channelCount & 0xFF); byte channelCountByte = (byte) (channelCount & 0xFF);
byte[] sampleRateDoubleBytes = getLongBytes(Double.doubleToLongBits(sampleRate)); byte[] sampleRateDoubleBytes = getLongBytes(Double.doubleToLongBits(sampleRate));
return element(0xAE, // TrackEntry return element(0xAE, // TrackEntry
...@@ -262,6 +290,9 @@ import java.util.List; ...@@ -262,6 +290,9 @@ import java.util.List;
element(0xE1, // Audio element(0xE1, // Audio
element(0x9F, channelCountByte), // Channels element(0x9F, channelCountByte), // Channels
element(0xB5, sampleRateDoubleBytes)), // SamplingFrequency element(0xB5, sampleRateDoubleBytes)), // SamplingFrequency
// DefaultDuration
defaultDurationNs != NO_VALUE ? element(0x23E383, getIntegerBytes(defaultDurationNs))
: empty(),
element(0x63A2, codecPrivate)); // CodecPrivate element(0x63A2, codecPrivate)); // CodecPrivate
} }
...@@ -272,12 +303,20 @@ import java.util.List; ...@@ -272,12 +303,20 @@ import java.util.List;
} }
private static EbmlElement createSimpleBlock(int trackNumber, int timecode, int flags, private static EbmlElement createSimpleBlock(int trackNumber, int timecode, int flags,
boolean encrypted, boolean validSignalByte, byte[] data) { boolean encrypted, boolean validSignalByte, int lacingFrameCount, byte[] data) {
byte[] trackNumberBytes = getIntegerBytes(trackNumber); byte[] trackNumberBytes = getIntegerBytes(trackNumber);
byte[] timeBytes = getIntegerBytes(timecode); byte[] timeBytes = getIntegerBytes(timecode);
byte[] simpleBlockBytes = createByteArray( byte[] simpleBlockBytes;
if (lacingFrameCount > 1) {
flags |= 0x04; // Fixed-size lacing
simpleBlockBytes = createByteArray(
0x40, trackNumberBytes[3], // Track number size=2
timeBytes[2], timeBytes[3], flags, lacingFrameCount - 1); // Timecode, flags and lacing.
} else {
simpleBlockBytes = createByteArray(
0x40, trackNumberBytes[3], // Track number size=2 0x40, trackNumberBytes[3], // Track number size=2
timeBytes[2], timeBytes[3], flags); // Timecode and flags timeBytes[2], timeBytes[3], flags); // Timecode and flags
}
if (encrypted) { if (encrypted) {
simpleBlockBytes = joinByteArrays( simpleBlockBytes = joinByteArrays(
simpleBlockBytes, createByteArray(validSignalByte ? 0x01 : 0x80), simpleBlockBytes, createByteArray(validSignalByte ? 0x01 : 0x80),
...@@ -302,14 +341,6 @@ import java.util.List; ...@@ -302,14 +341,6 @@ import java.util.List;
block); block);
} }
private static byte[] createByteArray(int... intArray) {
byte[] byteArray = new byte[intArray.length];
for (int i = 0; i < byteArray.length; i++) {
byteArray[i] = (byte) intArray[i];
}
return byteArray;
}
private static byte[] getIntegerBytes(int value) { private static byte[] getIntegerBytes(int value) {
return createByteArray( return createByteArray(
(value & 0xFF000000) >> 24, (value & 0xFF000000) >> 24,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment