mirror of
https://github.com/samsonjs/media.git
synced 2026-04-27 15:07:40 +00:00
parent
6c2b3c875d
commit
79db618ba6
3 changed files with 181 additions and 72 deletions
|
|
@ -113,7 +113,9 @@ public final class WebmExtractor implements Extractor {
|
||||||
private static final int ID_CONTENT_ENCODING = 0x6240;
|
private static final int ID_CONTENT_ENCODING = 0x6240;
|
||||||
private static final int ID_CONTENT_ENCODING_ORDER = 0x5031;
|
private static final int ID_CONTENT_ENCODING_ORDER = 0x5031;
|
||||||
private static final int ID_CONTENT_ENCODING_SCOPE = 0x5032;
|
private static final int ID_CONTENT_ENCODING_SCOPE = 0x5032;
|
||||||
private static final int ID_CONTENT_ENCODING_TYPE = 0x5033;
|
private static final int ID_CONTENT_COMPRESSION = 0x5034;
|
||||||
|
private static final int ID_CONTENT_COMPRESSION_ALGORITHM = 0x4254;
|
||||||
|
private static final int ID_CONTENT_COMPRESSION_SETTINGS = 0x4255;
|
||||||
private static final int ID_CONTENT_ENCRYPTION = 0x5035;
|
private static final int ID_CONTENT_ENCRYPTION = 0x5035;
|
||||||
private static final int ID_CONTENT_ENCRYPTION_ALGORITHM = 0x47E1;
|
private static final int ID_CONTENT_ENCRYPTION_ALGORITHM = 0x47E1;
|
||||||
private static final int ID_CONTENT_ENCRYPTION_KEY_ID = 0x47E2;
|
private static final int ID_CONTENT_ENCRYPTION_KEY_ID = 0x47E2;
|
||||||
|
|
@ -139,6 +141,7 @@ public final class WebmExtractor implements Extractor {
|
||||||
private final ParsableByteArray scratch;
|
private final ParsableByteArray scratch;
|
||||||
private final ParsableByteArray vorbisNumPageSamples;
|
private final ParsableByteArray vorbisNumPageSamples;
|
||||||
private final ParsableByteArray seekEntryIdBytes;
|
private final ParsableByteArray seekEntryIdBytes;
|
||||||
|
private final ParsableByteArray sampleStrippedBytes;
|
||||||
|
|
||||||
private long segmentContentPosition = UNKNOWN;
|
private long segmentContentPosition = UNKNOWN;
|
||||||
private long segmentContentSize = UNKNOWN;
|
private long segmentContentSize = UNKNOWN;
|
||||||
|
|
@ -178,7 +181,7 @@ public final class WebmExtractor implements Extractor {
|
||||||
|
|
||||||
// Sample reading state.
|
// Sample reading state.
|
||||||
private int sampleBytesRead;
|
private int sampleBytesRead;
|
||||||
private boolean sampleEncryptionDataRead;
|
private boolean sampleEncodingHandled;
|
||||||
private int sampleCurrentNalBytesRemaining;
|
private int sampleCurrentNalBytesRemaining;
|
||||||
private int sampleBytesWritten;
|
private int sampleBytesWritten;
|
||||||
private boolean sampleRead;
|
private boolean sampleRead;
|
||||||
|
|
@ -200,6 +203,7 @@ public final class WebmExtractor implements Extractor {
|
||||||
seekEntryIdBytes = new ParsableByteArray(4);
|
seekEntryIdBytes = new ParsableByteArray(4);
|
||||||
nalStartCode = new ParsableByteArray(NalUnitUtil.NAL_START_CODE);
|
nalStartCode = new ParsableByteArray(NalUnitUtil.NAL_START_CODE);
|
||||||
nalLength = new ParsableByteArray(4);
|
nalLength = new ParsableByteArray(4);
|
||||||
|
sampleStrippedBytes = new ParsableByteArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -213,10 +217,7 @@ public final class WebmExtractor implements Extractor {
|
||||||
blockState = BLOCK_STATE_START;
|
blockState = BLOCK_STATE_START;
|
||||||
reader.reset();
|
reader.reset();
|
||||||
varintReader.reset();
|
varintReader.reset();
|
||||||
sampleCurrentNalBytesRemaining = 0;
|
resetSample();
|
||||||
sampleBytesRead = 0;
|
|
||||||
sampleBytesWritten = 0;
|
|
||||||
sampleEncryptionDataRead = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -247,6 +248,7 @@ public final class WebmExtractor implements Extractor {
|
||||||
case ID_VIDEO:
|
case ID_VIDEO:
|
||||||
case ID_CONTENT_ENCODINGS:
|
case ID_CONTENT_ENCODINGS:
|
||||||
case ID_CONTENT_ENCODING:
|
case ID_CONTENT_ENCODING:
|
||||||
|
case ID_CONTENT_COMPRESSION:
|
||||||
case ID_CONTENT_ENCRYPTION:
|
case ID_CONTENT_ENCRYPTION:
|
||||||
case ID_CONTENT_ENCRYPTION_AES_SETTINGS:
|
case ID_CONTENT_ENCRYPTION_AES_SETTINGS:
|
||||||
case ID_CUES:
|
case ID_CUES:
|
||||||
|
|
@ -269,7 +271,7 @@ public final class WebmExtractor implements Extractor {
|
||||||
case ID_CHANNELS:
|
case ID_CHANNELS:
|
||||||
case ID_CONTENT_ENCODING_ORDER:
|
case ID_CONTENT_ENCODING_ORDER:
|
||||||
case ID_CONTENT_ENCODING_SCOPE:
|
case ID_CONTENT_ENCODING_SCOPE:
|
||||||
case ID_CONTENT_ENCODING_TYPE:
|
case ID_CONTENT_COMPRESSION_ALGORITHM:
|
||||||
case ID_CONTENT_ENCRYPTION_ALGORITHM:
|
case ID_CONTENT_ENCRYPTION_ALGORITHM:
|
||||||
case ID_CONTENT_ENCRYPTION_AES_SETTINGS_CIPHER_MODE:
|
case ID_CONTENT_ENCRYPTION_AES_SETTINGS_CIPHER_MODE:
|
||||||
case ID_CUE_TIME:
|
case ID_CUE_TIME:
|
||||||
|
|
@ -280,6 +282,7 @@ public final class WebmExtractor implements Extractor {
|
||||||
case ID_CODEC_ID:
|
case ID_CODEC_ID:
|
||||||
return EbmlReader.TYPE_STRING;
|
return EbmlReader.TYPE_STRING;
|
||||||
case ID_SEEK_ID:
|
case ID_SEEK_ID:
|
||||||
|
case ID_CONTENT_COMPRESSION_SETTINGS:
|
||||||
case ID_CONTENT_ENCRYPTION_KEY_ID:
|
case ID_CONTENT_ENCRYPTION_KEY_ID:
|
||||||
case ID_SIMPLE_BLOCK:
|
case ID_SIMPLE_BLOCK:
|
||||||
case ID_BLOCK:
|
case ID_BLOCK:
|
||||||
|
|
@ -371,17 +374,20 @@ public final class WebmExtractor implements Extractor {
|
||||||
blockState = BLOCK_STATE_START;
|
blockState = BLOCK_STATE_START;
|
||||||
return;
|
return;
|
||||||
case ID_CONTENT_ENCODING:
|
case ID_CONTENT_ENCODING:
|
||||||
if (!trackFormat.hasContentEncryption) {
|
if (trackFormat.hasContentEncryption) {
|
||||||
// We found a ContentEncoding other than Encryption.
|
if (trackFormat.encryptionKeyId == null) {
|
||||||
throw new ParserException("Found an unsupported ContentEncoding");
|
throw new ParserException("Encrypted Track found but ContentEncKeyID was not found");
|
||||||
|
}
|
||||||
|
if (!sentDrmInitData) {
|
||||||
|
extractorOutput.drmInitData(
|
||||||
|
new DrmInitData.Universal(MimeTypes.VIDEO_WEBM, trackFormat.encryptionKeyId));
|
||||||
|
sentDrmInitData = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (trackFormat.encryptionKeyId == null) {
|
return;
|
||||||
throw new ParserException("Encrypted Track found but ContentEncKeyID was not found");
|
case ID_CONTENT_ENCODINGS:
|
||||||
}
|
if (trackFormat.hasContentEncryption && trackFormat.sampleStrippedBytes != null) {
|
||||||
if (!sentDrmInitData) {
|
throw new ParserException("Combining encryption and compression is not supported");
|
||||||
extractorOutput.drmInitData(
|
|
||||||
new DrmInitData.Universal(MimeTypes.VIDEO_WEBM, trackFormat.encryptionKeyId));
|
|
||||||
sentDrmInitData = true;
|
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
case ID_TRACK_ENTRY:
|
case ID_TRACK_ENTRY:
|
||||||
|
|
@ -474,16 +480,15 @@ public final class WebmExtractor implements Extractor {
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
case ID_CONTENT_ENCODING_SCOPE:
|
case ID_CONTENT_ENCODING_SCOPE:
|
||||||
// This extractor only supports the scope of all frames (since that's the only scope used
|
// This extractor only supports the scope of all frames.
|
||||||
// for Encryption).
|
|
||||||
if (value != 1) {
|
if (value != 1) {
|
||||||
throw new ParserException("ContentEncodingScope " + value + " not supported");
|
throw new ParserException("ContentEncodingScope " + value + " not supported");
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
case ID_CONTENT_ENCODING_TYPE:
|
case ID_CONTENT_COMPRESSION_ALGORITHM:
|
||||||
// This extractor only supports Encrypted ContentEncodingType.
|
// This extractor only supports header stripping.
|
||||||
if (value != 1) {
|
if (value != 3) {
|
||||||
throw new ParserException("ContentEncodingType " + value + " not supported");
|
throw new ParserException("ContentCompAlgo " + value + " not supported");
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
case ID_CONTENT_ENCRYPTION_ALGORITHM:
|
case ID_CONTENT_ENCRYPTION_ALGORITHM:
|
||||||
|
|
@ -560,6 +565,11 @@ public final class WebmExtractor implements Extractor {
|
||||||
trackFormat.codecPrivate = new byte[contentSize];
|
trackFormat.codecPrivate = new byte[contentSize];
|
||||||
input.readFully(trackFormat.codecPrivate, 0, contentSize);
|
input.readFully(trackFormat.codecPrivate, 0, contentSize);
|
||||||
return;
|
return;
|
||||||
|
case ID_CONTENT_COMPRESSION_SETTINGS:
|
||||||
|
// This extractor only supports header stripping, so the payload is the stripped bytes.
|
||||||
|
trackFormat.sampleStrippedBytes = new byte[contentSize];
|
||||||
|
input.readFully(trackFormat.sampleStrippedBytes, 0, contentSize);
|
||||||
|
return;
|
||||||
case ID_CONTENT_ENCRYPTION_KEY_ID:
|
case ID_CONTENT_ENCRYPTION_KEY_ID:
|
||||||
trackFormat.encryptionKeyId = new byte[contentSize];
|
trackFormat.encryptionKeyId = new byte[contentSize];
|
||||||
input.readFully(trackFormat.encryptionKeyId, 0, contentSize);
|
input.readFully(trackFormat.encryptionKeyId, 0, contentSize);
|
||||||
|
|
@ -714,9 +724,15 @@ public final class WebmExtractor implements Extractor {
|
||||||
private void outputSampleMetadata(TrackOutput trackOutput, long timeUs) {
|
private void outputSampleMetadata(TrackOutput trackOutput, long timeUs) {
|
||||||
trackOutput.sampleMetadata(timeUs, blockFlags, sampleBytesWritten, 0, blockEncryptionKeyId);
|
trackOutput.sampleMetadata(timeUs, blockFlags, sampleBytesWritten, 0, blockEncryptionKeyId);
|
||||||
sampleRead = true;
|
sampleRead = true;
|
||||||
|
resetSample();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void resetSample() {
|
||||||
sampleBytesRead = 0;
|
sampleBytesRead = 0;
|
||||||
sampleBytesWritten = 0;
|
sampleBytesWritten = 0;
|
||||||
sampleEncryptionDataRead = false;
|
sampleCurrentNalBytesRemaining = 0;
|
||||||
|
sampleEncodingHandled = false;
|
||||||
|
sampleStrippedBytes.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -738,26 +754,30 @@ public final class WebmExtractor implements Extractor {
|
||||||
|
|
||||||
private void writeSampleData(ExtractorInput input, TrackOutput output, TrackFormat format,
|
private void writeSampleData(ExtractorInput input, TrackOutput output, TrackFormat format,
|
||||||
int size) throws IOException, InterruptedException {
|
int size) throws IOException, InterruptedException {
|
||||||
// Read the sample's encryption signal byte and set the IV size if necessary.
|
if (!sampleEncodingHandled) {
|
||||||
if (format.hasContentEncryption && !sampleEncryptionDataRead) {
|
if (format.hasContentEncryption) {
|
||||||
// Clear the encrypted flag.
|
// If the sample is encrypted, read its encryption signal byte and set the IV size.
|
||||||
blockFlags &= ~C.SAMPLE_FLAG_ENCRYPTED;
|
// Clear the encrypted flag.
|
||||||
input.readFully(scratch.data, 0, 1);
|
blockFlags &= ~C.SAMPLE_FLAG_ENCRYPTED;
|
||||||
sampleBytesRead++;
|
input.readFully(scratch.data, 0, 1);
|
||||||
if ((scratch.data[0] & 0x80) == 0x80) {
|
sampleBytesRead++;
|
||||||
throw new ParserException("Extension bit is set in signal byte");
|
if ((scratch.data[0] & 0x80) == 0x80) {
|
||||||
}
|
throw new ParserException("Extension bit is set in signal byte");
|
||||||
sampleEncryptionDataRead = true;
|
}
|
||||||
|
if ((scratch.data[0] & 0x01) == 0x01) {
|
||||||
// If the sample is encrypted, write the IV size instead of the signal byte, and set the flag.
|
scratch.data[0] = (byte) ENCRYPTION_IV_SIZE;
|
||||||
if ((scratch.data[0] & 0x01) == 0x01) {
|
scratch.setPosition(0);
|
||||||
scratch.data[0] = (byte) ENCRYPTION_IV_SIZE;
|
output.sampleData(scratch, 1);
|
||||||
scratch.setPosition(0);
|
sampleBytesWritten++;
|
||||||
output.sampleData(scratch, 1);
|
blockFlags |= C.SAMPLE_FLAG_ENCRYPTED;
|
||||||
sampleBytesWritten++;
|
}
|
||||||
blockFlags |= C.SAMPLE_FLAG_ENCRYPTED;
|
} else if (format.sampleStrippedBytes != null) {
|
||||||
|
// If the sample has header stripping, prepare to read/output the stripped bytes first.
|
||||||
|
sampleStrippedBytes.reset(format.sampleStrippedBytes, format.sampleStrippedBytes.length);
|
||||||
}
|
}
|
||||||
|
sampleEncodingHandled = true;
|
||||||
}
|
}
|
||||||
|
size += sampleStrippedBytes.limit();
|
||||||
|
|
||||||
if (CODEC_ID_H264.equals(format.codecId)) {
|
if (CODEC_ID_H264.equals(format.codecId)) {
|
||||||
// TODO: Deduplicate with Mp4Extractor.
|
// TODO: Deduplicate with Mp4Extractor.
|
||||||
|
|
@ -776,28 +796,23 @@ public final class WebmExtractor implements Extractor {
|
||||||
while (sampleBytesRead < size) {
|
while (sampleBytesRead < size) {
|
||||||
if (sampleCurrentNalBytesRemaining == 0) {
|
if (sampleCurrentNalBytesRemaining == 0) {
|
||||||
// Read the NAL length so that we know where we find the next one.
|
// Read the NAL length so that we know where we find the next one.
|
||||||
input.readFully(nalLengthData, nalUnitLengthFieldLengthDiff,
|
readToTarget(input, nalLengthData, nalUnitLengthFieldLengthDiff,
|
||||||
nalUnitLengthFieldLength);
|
nalUnitLengthFieldLength);
|
||||||
nalLength.setPosition(0);
|
nalLength.setPosition(0);
|
||||||
sampleCurrentNalBytesRemaining = nalLength.readUnsignedIntToInt();
|
sampleCurrentNalBytesRemaining = nalLength.readUnsignedIntToInt();
|
||||||
// Write a start code for the current NAL unit.
|
// Write a start code for the current NAL unit.
|
||||||
nalStartCode.setPosition(0);
|
nalStartCode.setPosition(0);
|
||||||
output.sampleData(nalStartCode, 4);
|
output.sampleData(nalStartCode, 4);
|
||||||
sampleBytesRead += nalUnitLengthFieldLength;
|
|
||||||
sampleBytesWritten += 4;
|
sampleBytesWritten += 4;
|
||||||
} else {
|
} else {
|
||||||
// Write the payload of the NAL unit.
|
// Write the payload of the NAL unit.
|
||||||
int writtenBytes = output.sampleData(input, sampleCurrentNalBytesRemaining);
|
sampleCurrentNalBytesRemaining -=
|
||||||
sampleCurrentNalBytesRemaining -= writtenBytes;
|
readToOutput(input, output, sampleCurrentNalBytesRemaining);
|
||||||
sampleBytesRead += writtenBytes;
|
|
||||||
sampleBytesWritten += writtenBytes;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
while (sampleBytesRead < size) {
|
while (sampleBytesRead < size) {
|
||||||
int writtenBytes = output.sampleData(input, size - sampleBytesRead);
|
readToOutput(input, output, size - sampleBytesRead);
|
||||||
sampleBytesRead += writtenBytes;
|
|
||||||
sampleBytesWritten += writtenBytes;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -814,6 +829,39 @@ public final class WebmExtractor implements Extractor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes {@code length} bytes of sample data into {@code target} at {@code offset}, consisting of
|
||||||
|
* pending {@link #sampleStrippedBytes} and any remaining data read from {@code input}.
|
||||||
|
*/
|
||||||
|
private void readToTarget(ExtractorInput input, byte[] target, int offset, int length)
|
||||||
|
throws IOException, InterruptedException {
|
||||||
|
int pendingStrippedBytes = Math.min(length, sampleStrippedBytes.bytesLeft());
|
||||||
|
input.readFully(target, offset + pendingStrippedBytes, length - pendingStrippedBytes);
|
||||||
|
if (pendingStrippedBytes > 0) {
|
||||||
|
sampleStrippedBytes.readBytes(target, offset, pendingStrippedBytes);
|
||||||
|
}
|
||||||
|
sampleBytesRead += length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Outputs up to {@code length} bytes of sample data to {@code output}, consisting of either
|
||||||
|
* {@link #sampleStrippedBytes} or data read from {@code input}.
|
||||||
|
*/
|
||||||
|
private int readToOutput(ExtractorInput input, TrackOutput output, int length)
|
||||||
|
throws IOException, InterruptedException {
|
||||||
|
int bytesRead;
|
||||||
|
int strippedBytesLeft = sampleStrippedBytes.bytesLeft();
|
||||||
|
if (strippedBytesLeft > 0) {
|
||||||
|
bytesRead = Math.min(length, strippedBytesLeft);
|
||||||
|
output.sampleData(sampleStrippedBytes, bytesRead);
|
||||||
|
} else {
|
||||||
|
bytesRead = output.sampleData(input, length);
|
||||||
|
}
|
||||||
|
sampleBytesRead += bytesRead;
|
||||||
|
sampleBytesWritten += bytesRead;
|
||||||
|
return bytesRead;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builds a {@link ChunkIndex} containing recently gathered Cues information.
|
* Builds a {@link ChunkIndex} containing recently gathered Cues information.
|
||||||
*
|
*
|
||||||
|
|
@ -958,6 +1006,7 @@ public final class WebmExtractor implements Extractor {
|
||||||
public int type = UNKNOWN;
|
public int type = UNKNOWN;
|
||||||
public int defaultSampleDurationNs = UNKNOWN;
|
public int defaultSampleDurationNs = UNKNOWN;
|
||||||
public boolean hasContentEncryption;
|
public boolean hasContentEncryption;
|
||||||
|
public byte[] sampleStrippedBytes;
|
||||||
public byte[] encryptionKeyId;
|
public byte[] encryptionKeyId;
|
||||||
public byte[] codecPrivate;
|
public byte[] codecPrivate;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ import java.util.List;
|
||||||
*/
|
*/
|
||||||
/* package */ final class StreamBuilder {
|
/* 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 {
|
public static final class ContentEncodingSettings {
|
||||||
|
|
||||||
private final int order;
|
private final int order;
|
||||||
|
|
@ -36,14 +36,24 @@ import java.util.List;
|
||||||
private final int type;
|
private final int type;
|
||||||
private final int algorithm;
|
private final int algorithm;
|
||||||
private final int aesCipherMode;
|
private final int aesCipherMode;
|
||||||
|
private final byte[] strippedBytes;
|
||||||
|
|
||||||
public ContentEncodingSettings(int order, int scope, int type, int algorithm,
|
public ContentEncodingSettings(int order, int scope, int algorithm, int aesCipherMode) {
|
||||||
int aesCipherMode) {
|
|
||||||
this.order = order;
|
this.order = order;
|
||||||
this.scope = scope;
|
this.scope = scope;
|
||||||
this.type = type;
|
this.type = 1; // Encryption
|
||||||
this.algorithm = algorithm;
|
this.algorithm = algorithm;
|
||||||
this.aesCipherMode = aesCipherMode;
|
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);
|
byte[] heightBytes = getIntegerBytes(pixelHeight);
|
||||||
EbmlElement contentEncodingSettingsElement;
|
EbmlElement contentEncodingSettingsElement;
|
||||||
if (contentEncodingSettings != null) {
|
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 =
|
contentEncodingSettingsElement =
|
||||||
element(0x6D80, // ContentEncodings
|
element(0x6D80, // ContentEncodings
|
||||||
element(0x6240, // ContentEncoding
|
element(0x6240, // ContentEncoding
|
||||||
|
|
@ -234,13 +261,7 @@ import java.util.List;
|
||||||
element(0x5032, (byte) (contentEncodingSettings.scope & 0xFF)),
|
element(0x5032, (byte) (contentEncodingSettings.scope & 0xFF)),
|
||||||
// ContentEncodingType
|
// ContentEncodingType
|
||||||
element(0x5033, (byte) (contentEncodingSettings.type & 0xFF)),
|
element(0x5033, (byte) (contentEncodingSettings.type & 0xFF)),
|
||||||
element(0x5035, // ContentEncryption
|
encryptionOrCompressionElement));
|
||||||
// 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 {
|
} else {
|
||||||
contentEncodingSettingsElement = empty();
|
contentEncodingSettingsElement = empty();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -205,7 +205,7 @@ public final class WebmExtractorTest extends InstrumentationTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testPrepareContentEncodingEncryption() throws IOException, InterruptedException {
|
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()
|
byte[] data = new StreamBuilder()
|
||||||
.setHeader(WEBM_DOC_TYPE)
|
.setHeader(WEBM_DOC_TYPE)
|
||||||
.setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_US)
|
.setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_US)
|
||||||
|
|
@ -305,7 +305,7 @@ public final class WebmExtractorTest extends InstrumentationTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testPrepareInvalidContentEncodingOrder() throws IOException, InterruptedException {
|
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()
|
byte[] data = new StreamBuilder()
|
||||||
.setHeader(WEBM_DOC_TYPE)
|
.setHeader(WEBM_DOC_TYPE)
|
||||||
.setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_US)
|
.setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_US)
|
||||||
|
|
@ -320,7 +320,7 @@ public final class WebmExtractorTest extends InstrumentationTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testPrepareInvalidContentEncodingScope() throws IOException, InterruptedException {
|
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()
|
byte[] data = new StreamBuilder()
|
||||||
.setHeader(WEBM_DOC_TYPE)
|
.setHeader(WEBM_DOC_TYPE)
|
||||||
.setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_US)
|
.setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_US)
|
||||||
|
|
@ -334,8 +334,9 @@ public final class WebmExtractorTest extends InstrumentationTestCase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testPrepareInvalidContentEncodingType() throws IOException, InterruptedException {
|
public void testPrepareInvalidContentCompAlgo()
|
||||||
ContentEncodingSettings settings = new ContentEncodingSettings(0, 1, 0, 5, 1);
|
throws IOException, InterruptedException {
|
||||||
|
ContentEncodingSettings settings = new ContentEncodingSettings(0, 1, 0, new byte[0]);
|
||||||
byte[] data = new StreamBuilder()
|
byte[] data = new StreamBuilder()
|
||||||
.setHeader(WEBM_DOC_TYPE)
|
.setHeader(WEBM_DOC_TYPE)
|
||||||
.setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_US)
|
.setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_US)
|
||||||
|
|
@ -345,12 +346,12 @@ public final class WebmExtractorTest extends InstrumentationTestCase {
|
||||||
TestUtil.consumeTestData(extractor, data);
|
TestUtil.consumeTestData(extractor, data);
|
||||||
fail();
|
fail();
|
||||||
} catch (ParserException exception) {
|
} catch (ParserException exception) {
|
||||||
assertEquals("ContentEncodingType 0 not supported", exception.getMessage());
|
assertEquals("ContentCompAlgo 0 not supported", exception.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testPrepareInvalidContentEncAlgo() throws IOException, InterruptedException {
|
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()
|
byte[] data = new StreamBuilder()
|
||||||
.setHeader(WEBM_DOC_TYPE)
|
.setHeader(WEBM_DOC_TYPE)
|
||||||
.setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_US)
|
.setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_US)
|
||||||
|
|
@ -365,7 +366,7 @@ public final class WebmExtractorTest extends InstrumentationTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testPrepareInvalidAESSettingsCipherMode() throws IOException, InterruptedException {
|
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()
|
byte[] data = new StreamBuilder()
|
||||||
.setHeader(WEBM_DOC_TYPE)
|
.setHeader(WEBM_DOC_TYPE)
|
||||||
.setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_US)
|
.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());
|
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 {
|
public void testReadTwoTrackSamples() throws IOException, InterruptedException {
|
||||||
byte[] media = createFrameData(100);
|
byte[] media = createFrameData(100);
|
||||||
byte[] data = new StreamBuilder()
|
byte[] data = new StreamBuilder()
|
||||||
|
|
@ -479,7 +518,7 @@ public final class WebmExtractorTest extends InstrumentationTestCase {
|
||||||
|
|
||||||
public void testReadEncryptedFrame() throws IOException, InterruptedException {
|
public void testReadEncryptedFrame() throws IOException, InterruptedException {
|
||||||
byte[] media = createFrameData(100);
|
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()
|
byte[] data = new StreamBuilder()
|
||||||
.setHeader(WEBM_DOC_TYPE)
|
.setHeader(WEBM_DOC_TYPE)
|
||||||
.setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_US)
|
.setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_US)
|
||||||
|
|
@ -498,7 +537,7 @@ public final class WebmExtractorTest extends InstrumentationTestCase {
|
||||||
public void testReadEncryptedFrameWithInvalidSignalByte()
|
public void testReadEncryptedFrameWithInvalidSignalByte()
|
||||||
throws IOException, InterruptedException {
|
throws IOException, InterruptedException {
|
||||||
byte[] media = createFrameData(100);
|
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()
|
byte[] data = new StreamBuilder()
|
||||||
.setHeader(WEBM_DOC_TYPE)
|
.setHeader(WEBM_DOC_TYPE)
|
||||||
.setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_US)
|
.setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_US)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue