Commit 79db618b by Oliver Woodman

Add support for header stripping in Matroska streams.

Issue: #589
parent 6c2b3c87
......@@ -28,7 +28,7 @@ import java.util.List;
*/
/* package */ final class StreamBuilder {
/** Used by {@link #addVp9Track} to create a Track header with Encryption. */
/** Used by {@link #addVp9Track} to create a track header with encryption/compression. */
public static final class ContentEncodingSettings {
private final int order;
......@@ -36,14 +36,24 @@ import java.util.List;
private final int type;
private final int algorithm;
private final int aesCipherMode;
private final byte[] strippedBytes;
public ContentEncodingSettings(int order, int scope, int type, int algorithm,
int aesCipherMode) {
public ContentEncodingSettings(int order, int scope, int algorithm, int aesCipherMode) {
this.order = order;
this.scope = scope;
this.type = type;
this.type = 1; // Encryption
this.algorithm = algorithm;
this.aesCipherMode = aesCipherMode;
this.strippedBytes = null;
}
public ContentEncodingSettings(int order, int scope, int algorithm, byte[] strippedBytes) {
this.order = order;
this.scope = scope;
this.type = 0; // Compression
this.algorithm = algorithm;
this.aesCipherMode = 0;
this.strippedBytes = strippedBytes;
}
}
......@@ -225,6 +235,23 @@ import java.util.List;
byte[] heightBytes = getIntegerBytes(pixelHeight);
EbmlElement contentEncodingSettingsElement;
if (contentEncodingSettings != null) {
EbmlElement encryptionOrCompressionElement;
if (contentEncodingSettings.type == 0) {
encryptionOrCompressionElement = element(0x5034, // ContentCompression
element(0x4254, (byte) (contentEncodingSettings.algorithm & 0xFF)), // ContentCompAlgo
element(0x4255, contentEncodingSettings.strippedBytes)); // ContentCompSettings
} else if (contentEncodingSettings.type == 1) {
encryptionOrCompressionElement = element(0x5035, // ContentEncryption
// ContentEncAlgo
element(0x47E1, (byte) (contentEncodingSettings.algorithm & 0xFF)),
element(0x47E2, TEST_ENCRYPTION_KEY_ID), // ContentEncKeyID
element(0x47E7, // ContentEncAESSettings
// AESSettingsCipherMode
element(0x47E8, (byte) (contentEncodingSettings.aesCipherMode & 0xFF))));
} else {
throw new IllegalArgumentException("Unexpected encoding type.");
}
contentEncodingSettingsElement =
element(0x6D80, // ContentEncodings
element(0x6240, // ContentEncoding
......@@ -234,13 +261,7 @@ import java.util.List;
element(0x5032, (byte) (contentEncodingSettings.scope & 0xFF)),
// ContentEncodingType
element(0x5033, (byte) (contentEncodingSettings.type & 0xFF)),
element(0x5035, // ContentEncryption
// ContentEncAlgo
element(0x47E1, (byte) (contentEncodingSettings.algorithm & 0xFF)),
element(0x47E2, TEST_ENCRYPTION_KEY_ID), // ContentEncKeyID
element(0x47E7, // ContentEncAESSettings
// AESSettingsCipherMode
element(0x47E8, (byte) (contentEncodingSettings.aesCipherMode & 0xFF))))));
encryptionOrCompressionElement));
} else {
contentEncodingSettingsElement = empty();
}
......
......@@ -205,7 +205,7 @@ public final class WebmExtractorTest extends InstrumentationTestCase {
}
public void testPrepareContentEncodingEncryption() throws IOException, InterruptedException {
ContentEncodingSettings settings = new StreamBuilder.ContentEncodingSettings(0, 1, 1, 5, 1);
ContentEncodingSettings settings = new StreamBuilder.ContentEncodingSettings(0, 1, 5, 1);
byte[] data = new StreamBuilder()
.setHeader(WEBM_DOC_TYPE)
.setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_US)
......@@ -305,7 +305,7 @@ public final class WebmExtractorTest extends InstrumentationTestCase {
}
public void testPrepareInvalidContentEncodingOrder() throws IOException, InterruptedException {
ContentEncodingSettings settings = new ContentEncodingSettings(1, 1, 1, 5, 1);
ContentEncodingSettings settings = new ContentEncodingSettings(1, 1, 5, 1);
byte[] data = new StreamBuilder()
.setHeader(WEBM_DOC_TYPE)
.setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_US)
......@@ -320,7 +320,7 @@ public final class WebmExtractorTest extends InstrumentationTestCase {
}
public void testPrepareInvalidContentEncodingScope() throws IOException, InterruptedException {
ContentEncodingSettings settings = new ContentEncodingSettings(0, 0, 1, 5, 1);
ContentEncodingSettings settings = new ContentEncodingSettings(0, 0, 5, 1);
byte[] data = new StreamBuilder()
.setHeader(WEBM_DOC_TYPE)
.setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_US)
......@@ -334,8 +334,9 @@ public final class WebmExtractorTest extends InstrumentationTestCase {
}
}
public void testPrepareInvalidContentEncodingType() throws IOException, InterruptedException {
ContentEncodingSettings settings = new ContentEncodingSettings(0, 1, 0, 5, 1);
public void testPrepareInvalidContentCompAlgo()
throws IOException, InterruptedException {
ContentEncodingSettings settings = new ContentEncodingSettings(0, 1, 0, new byte[0]);
byte[] data = new StreamBuilder()
.setHeader(WEBM_DOC_TYPE)
.setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_US)
......@@ -345,12 +346,12 @@ public final class WebmExtractorTest extends InstrumentationTestCase {
TestUtil.consumeTestData(extractor, data);
fail();
} catch (ParserException exception) {
assertEquals("ContentEncodingType 0 not supported", exception.getMessage());
assertEquals("ContentCompAlgo 0 not supported", exception.getMessage());
}
}
public void testPrepareInvalidContentEncAlgo() throws IOException, InterruptedException {
ContentEncodingSettings settings = new ContentEncodingSettings(0, 1, 1, 4, 1);
ContentEncodingSettings settings = new ContentEncodingSettings(0, 1, 4, 1);
byte[] data = new StreamBuilder()
.setHeader(WEBM_DOC_TYPE)
.setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_US)
......@@ -365,7 +366,7 @@ public final class WebmExtractorTest extends InstrumentationTestCase {
}
public void testPrepareInvalidAESSettingsCipherMode() throws IOException, InterruptedException {
ContentEncodingSettings settings = new ContentEncodingSettings(0, 1, 1, 5, 0);
ContentEncodingSettings settings = new ContentEncodingSettings(0, 1, 5, 0);
byte[] data = new StreamBuilder()
.setHeader(WEBM_DOC_TYPE)
.setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_US)
......@@ -395,6 +396,44 @@ public final class WebmExtractorTest extends InstrumentationTestCase {
assertSample(0, media, 0, true, false, null, getVideoOutput());
}
public void testReadSampleKeyframeStripped() throws IOException, InterruptedException {
byte[] strippedBytes = new byte[] {-1, -1};
ContentEncodingSettings settings = new ContentEncodingSettings(0, 1, 3, strippedBytes);
byte[] sampleBytes = createFrameData(100);
byte[] unstrippedSampleBytes = TestUtil.joinByteArrays(strippedBytes, sampleBytes);
byte[] data = new StreamBuilder()
.setHeader(WEBM_DOC_TYPE)
.setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_US)
.addVp9Track(TEST_WIDTH, TEST_HEIGHT, settings)
.addSimpleBlockMedia(1 /* trackNumber */, 0 /* clusterTimecode */, 0 /* blockTimecode */,
true /* keyframe */, false /* invisible */, sampleBytes)
.build(1);
TestUtil.consumeTestData(extractor, data);
assertVp9VideoFormat();
assertSample(0, unstrippedSampleBytes, 0, true, false, null, getVideoOutput());
}
public void testReadSampleKeyframeManyBytesStripped() throws IOException, InterruptedException {
byte[] strippedBytes = createFrameData(100);
ContentEncodingSettings settings = new ContentEncodingSettings(0, 1, 3, strippedBytes);
byte[] sampleBytes = createFrameData(5);
byte[] unstrippedSampleBytes = TestUtil.joinByteArrays(strippedBytes, sampleBytes);
byte[] data = new StreamBuilder()
.setHeader(WEBM_DOC_TYPE)
.setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_US)
.addVp9Track(TEST_WIDTH, TEST_HEIGHT, settings)
.addSimpleBlockMedia(1 /* trackNumber */, 0 /* clusterTimecode */, 0 /* blockTimecode */,
true /* keyframe */, false /* invisible */, sampleBytes)
.build(1);
TestUtil.consumeTestData(extractor, data);
assertVp9VideoFormat();
assertSample(0, unstrippedSampleBytes, 0, true, false, null, getVideoOutput());
}
public void testReadTwoTrackSamples() throws IOException, InterruptedException {
byte[] media = createFrameData(100);
byte[] data = new StreamBuilder()
......@@ -479,7 +518,7 @@ public final class WebmExtractorTest extends InstrumentationTestCase {
public void testReadEncryptedFrame() throws IOException, InterruptedException {
byte[] media = createFrameData(100);
ContentEncodingSettings settings = new ContentEncodingSettings(0, 1, 1, 5, 1);
ContentEncodingSettings settings = new ContentEncodingSettings(0, 1, 5, 1);
byte[] data = new StreamBuilder()
.setHeader(WEBM_DOC_TYPE)
.setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_US)
......@@ -498,7 +537,7 @@ public final class WebmExtractorTest extends InstrumentationTestCase {
public void testReadEncryptedFrameWithInvalidSignalByte()
throws IOException, InterruptedException {
byte[] media = createFrameData(100);
ContentEncodingSettings settings = new ContentEncodingSettings(0, 1, 1, 5, 1);
ContentEncodingSettings settings = new ContentEncodingSettings(0, 1, 5, 1);
byte[] data = new StreamBuilder()
.setHeader(WEBM_DOC_TYPE)
.setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_US)
......
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