Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add AC-4 DRM Support #6797

Merged
merged 7 commits into from
Jan 10, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1265,10 +1265,17 @@ private boolean readSample(ExtractorInput input) throws IOException, Interrupted
sampleSize -= Atom.HEADER_SIZE;
input.skipFully(Atom.HEADER_SIZE);
}
sampleBytesWritten = currentTrackBundle.outputSampleEncryptionData();
sampleSize += sampleBytesWritten;
if (MimeTypes.AUDIO_AC4.equals(currentTrackBundle.track.format.sampleMimeType)) {
Ac4Util.getAc4SampleHeader(sampleSize, scratch);

boolean isAc4HeaderRequired =
MimeTypes.AUDIO_AC4.equals(currentTrackBundle.track.format.sampleMimeType);

int encryptionDataBytesWritten = currentTrackBundle.outputSampleEncryptionData(
sampleSize, isAc4HeaderRequired ? Ac4Util.SAMPLE_HEADER_SIZE : 0);
sampleBytesWritten = encryptionDataBytesWritten;
sampleSize += encryptionDataBytesWritten;

if (isAc4HeaderRequired) {
Ac4Util.getAc4SampleHeader(sampleSize - encryptionDataBytesWritten, scratch);
currentTrackBundle.output.sampleData(scratch, Ac4Util.SAMPLE_HEADER_SIZE);
sampleBytesWritten += Ac4Util.SAMPLE_HEADER_SIZE;
sampleSize += Ac4Util.SAMPLE_HEADER_SIZE;
Expand Down Expand Up @@ -1555,9 +1562,13 @@ public boolean next() {
/**
* Outputs the encryption data for the current sample.
*
* @param sampleSize The size of the current sample in bytes, excluding any additional clear
* header that will be prefixed to the sample by the extractor.
* @param clearHeaderSize The size of a clear header that will be prefixed to the sample by the
* extractor, or 0.
* @return The number of written bytes.
*/
public int outputSampleEncryptionData() {
public int outputSampleEncryptionData(int sampleSize, int clearHeaderSize) {
TrackEncryptionBox encryptionBox = getEncryptionBoxIfEncrypted();
if (encryptionBox == null) {
return 0;
Expand All @@ -1576,24 +1587,66 @@ public int outputSampleEncryptionData() {
vectorSize = initVectorData.length;
}

boolean subsampleEncryption = fragment.sampleHasSubsampleEncryptionTable(currentSampleIndex);
boolean haveSubsampleEncryptionTable =
fragment.sampleHasSubsampleEncryptionTable(currentSampleIndex);
boolean writeSubsampleEncryptionData =
haveSubsampleEncryptionTable | clearHeaderSize != 0;

// Write the signal byte, containing the vector size and the subsample encryption flag.
encryptionSignalByte.data[0] = (byte) (vectorSize | (subsampleEncryption ? 0x80 : 0));
encryptionSignalByte.data[0] =
(byte) (vectorSize | (writeSubsampleEncryptionData ? 0x80 : 0));
encryptionSignalByte.setPosition(0);
output.sampleData(encryptionSignalByte, 1);
// Write the vector.
output.sampleData(initializationVectorData, vectorSize);
// If we don't have subsample encryption data, we're done.
if (!subsampleEncryption) {

if (!writeSubsampleEncryptionData) {
return 1 + vectorSize;
}
// Write the subsample encryption data.

if (!haveSubsampleEncryptionTable) {
// Need to synthesize subsample encryption data. The sample is fully encrypted except
// for the additional header that the extractor is going to prefix, so we need to write the
// following to output.sampleData:
// subsampleCount (unsigned short) = 1
// clearDataSizes[0] (unsigned short) = clearHeaderSize
// encryptedDataSizes[0] (unsigned int) = sampleSize
ParsableByteArray encryptionData = new ParsableByteArray(8);
encryptionData.data[0] = (byte)0;
encryptionData.data[1] = (byte)1;
encryptionData.data[2] = (byte)((clearHeaderSize & 0xFF00) >>> 8);
encryptionData.data[3] = (byte)( clearHeaderSize & 0x00FF);
encryptionData.data[4] = (byte)((sampleSize & 0xFF000000) >>> 24);
encryptionData.data[5] = (byte)((sampleSize & 0x00FF0000) >>> 16);
encryptionData.data[6] = (byte)((sampleSize & 0x0000FF00) >>> 8);
encryptionData.data[7] = (byte)( sampleSize & 0x000000FF);
encryptionData.setPosition(0);
output.sampleData(encryptionData, 8);
return 1 + vectorSize + 8;
}

ParsableByteArray subsampleEncryptionData = fragment.sampleEncryptionData;
int subsampleCount = subsampleEncryptionData.readUnsignedShort();
subsampleEncryptionData.skipBytes(-2);
int subsampleDataLength = 2 + 6 * subsampleCount;
output.sampleData(subsampleEncryptionData, subsampleDataLength);

if (clearHeaderSize > 0) {
// On the way through, we need to re-write the 3rd and 4th bytes, which hold
// clearDataSizes[0], so that clearHeaderSize is added into the value. This must be done
// without modifying subsampleEncryptionData itself.
ParsableByteArray subsampleEncryptionData2 = new ParsableByteArray(subsampleDataLength);
subsampleEncryptionData2.readBytes(subsampleEncryptionData.data,
subsampleEncryptionData.getPosition(), subsampleDataLength);
int clearDataSize = (subsampleEncryptionData2.data[2] & 0xFF) << 8
| (subsampleEncryptionData2.data[3] & 0xFF) + clearHeaderSize;
subsampleEncryptionData2.data[2] = (byte)((clearDataSize & 0xFF00) >>> 8);
subsampleEncryptionData2.data[3] = (byte)( clearDataSize & 0x00FF);
subsampleEncryptionData2.setPosition(0);
output.sampleData(subsampleEncryptionData2, subsampleDataLength);
subsampleEncryptionData.skipBytes(subsampleDataLength);
} else {
output.sampleData(subsampleEncryptionData, subsampleDataLength);
}
return 1 + vectorSize + subsampleDataLength;
}

Expand Down
Binary file not shown.
145 changes: 145 additions & 0 deletions library/core/src/test/assets/mp4/sample_ac4_protected.mp4.0.dump
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
seekMap:
isSeekable = true
duration = 760000
getPosition(0) = [[timeUs=0, position=950]]
numberOfTracks = 1
track 0:
format:
bitrate = -1
id = 1
containerMimeType = null
sampleMimeType = audio/ac4
maxInputSize = -1
width = -1
height = -1
frameRate = -1.0
rotationDegrees = 0
pixelWidthHeightRatio = 1.0
channelCount = 2
sampleRate = 48000
pcmEncoding = -1
encoderDelay = 0
encoderPadding = 0
subsampleOffsetUs = 9223372036854775807
selectionFlags = 0
language = und
drmInitData = -1683793742
metadata = null
initializationData:
total output bytes = 7936
sample count = 19
sample 0:
time = 0
flags = 1073741825
data = length 384, hash 96EFFFF3
crypto mode = 1
encryption key = length 16, hash 9FDDEA52
sample 1:
time = 40000
flags = 1073741825
data = length 384, hash 899279C6
crypto mode = 1
encryption key = length 16, hash 9FDDEA52
sample 2:
time = 80000
flags = 1073741825
data = length 384, hash 9EA9F45
crypto mode = 1
encryption key = length 16, hash 9FDDEA52
sample 3:
time = 120000
flags = 1073741825
data = length 384, hash 82D362A9
crypto mode = 1
encryption key = length 16, hash 9FDDEA52
sample 4:
time = 160000
flags = 1073741825
data = length 384, hash B8705CFB
crypto mode = 1
encryption key = length 16, hash 9FDDEA52
sample 5:
time = 200000
flags = 1073741825
data = length 384, hash 58B5628E
crypto mode = 1
encryption key = length 16, hash 9FDDEA52
sample 6:
time = 240000
flags = 1073741825
data = length 384, hash 87F3C13B
crypto mode = 1
encryption key = length 16, hash 9FDDEA52
sample 7:
time = 280000
flags = 1073741825
data = length 384, hash 54333DC5
crypto mode = 1
encryption key = length 16, hash 9FDDEA52
sample 8:
time = 320000
flags = 1073741825
data = length 384, hash 1C49C4B3
crypto mode = 1
encryption key = length 16, hash 9FDDEA52
sample 9:
time = 360000
flags = 1073741825
data = length 384, hash 5FDC324F
crypto mode = 1
encryption key = length 16, hash 9FDDEA52
sample 10:
time = 400000
flags = 1073741825
data = length 384, hash B2A7F444
crypto mode = 1
encryption key = length 16, hash 9FDDEA52
sample 11:
time = 440000
flags = 1073741825
data = length 512, hash 5FD06C1E
crypto mode = 1
encryption key = length 16, hash 9FDDEA52
sample 12:
time = 480000
flags = 1073741825
data = length 537, hash 7ABBDCB
crypto mode = 1
encryption key = length 16, hash 9FDDEA52
sample 13:
time = 520000
flags = 1073741825
data = length 616, hash 3F657E23
crypto mode = 1
encryption key = length 16, hash 9FDDEA52
sample 14:
time = 560000
flags = 1073741825
data = length 453, hash 8FCF0529
crypto mode = 1
encryption key = length 16, hash 9FDDEA52
sample 15:
time = 600000
flags = 1073741825
data = length 383, hash 7F8C9E19
crypto mode = 1
encryption key = length 16, hash 9FDDEA52
sample 16:
time = 640000
flags = 1073741825
data = length 410, hash 3727858D
crypto mode = 1
encryption key = length 16, hash 9FDDEA52
sample 17:
time = 680000
flags = 1073741825
data = length 391, hash E2931212
crypto mode = 1
encryption key = length 16, hash 9FDDEA52
sample 18:
time = 720000
flags = 1073741825
data = length 410, hash 63017D46
crypto mode = 1
encryption key = length 16, hash 9FDDEA52
tracksEnded = true
109 changes: 109 additions & 0 deletions library/core/src/test/assets/mp4/sample_ac4_protected.mp4.1.dump
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
seekMap:
isSeekable = true
duration = 760000
getPosition(0) = [[timeUs=0, position=950]]
numberOfTracks = 1
track 0:
format:
bitrate = -1
id = 1
containerMimeType = null
sampleMimeType = audio/ac4
maxInputSize = -1
width = -1
height = -1
frameRate = -1.0
rotationDegrees = 0
pixelWidthHeightRatio = 1.0
channelCount = 2
sampleRate = 48000
pcmEncoding = -1
encoderDelay = 0
encoderPadding = 0
subsampleOffsetUs = 9223372036854775807
selectionFlags = 0
language = und
drmInitData = -1683793742
metadata = null
initializationData:
total output bytes = 5632
sample count = 13
sample 0:
time = 240000
flags = 1073741825
data = length 384, hash 87F3C13B
crypto mode = 1
encryption key = length 16, hash 9FDDEA52
sample 1:
time = 280000
flags = 1073741825
data = length 384, hash 54333DC5
crypto mode = 1
encryption key = length 16, hash 9FDDEA52
sample 2:
time = 320000
flags = 1073741825
data = length 384, hash 1C49C4B3
crypto mode = 1
encryption key = length 16, hash 9FDDEA52
sample 3:
time = 360000
flags = 1073741825
data = length 384, hash 5FDC324F
crypto mode = 1
encryption key = length 16, hash 9FDDEA52
sample 4:
time = 400000
flags = 1073741825
data = length 384, hash B2A7F444
crypto mode = 1
encryption key = length 16, hash 9FDDEA52
sample 5:
time = 440000
flags = 1073741825
data = length 512, hash 5FD06C1E
crypto mode = 1
encryption key = length 16, hash 9FDDEA52
sample 6:
time = 480000
flags = 1073741825
data = length 537, hash 7ABBDCB
crypto mode = 1
encryption key = length 16, hash 9FDDEA52
sample 7:
time = 520000
flags = 1073741825
data = length 616, hash 3F657E23
crypto mode = 1
encryption key = length 16, hash 9FDDEA52
sample 8:
time = 560000
flags = 1073741825
data = length 453, hash 8FCF0529
crypto mode = 1
encryption key = length 16, hash 9FDDEA52
sample 9:
time = 600000
flags = 1073741825
data = length 383, hash 7F8C9E19
crypto mode = 1
encryption key = length 16, hash 9FDDEA52
sample 10:
time = 640000
flags = 1073741825
data = length 410, hash 3727858D
crypto mode = 1
encryption key = length 16, hash 9FDDEA52
sample 11:
time = 680000
flags = 1073741825
data = length 391, hash E2931212
crypto mode = 1
encryption key = length 16, hash 9FDDEA52
sample 12:
time = 720000
flags = 1073741825
data = length 410, hash 63017D46
crypto mode = 1
encryption key = length 16, hash 9FDDEA52
tracksEnded = true
Loading