From 4ce72d9d6d7cab11169430d69809ec55be694ec2 Mon Sep 17 00:00:00 2001 From: ybai001 Date: Thu, 9 Jan 2020 15:23:05 +0800 Subject: [PATCH] Update AC-4 DRM code based on comments Update cleardatasize[0] in extractor rather than sampleDataQueue. --- .../extractor/mp4/FragmentedMp4Extractor.java | 75 ++++++++++++++++--- .../exoplayer2/source/SampleDataQueue.java | 17 ++--- .../exoplayer2/source/SampleQueue.java | 3 +- 3 files changed, 70 insertions(+), 25 deletions(-) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/FragmentedMp4Extractor.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/FragmentedMp4Extractor.java index a4a70ce7e5..66ed1a5c92 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/FragmentedMp4Extractor.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/FragmentedMp4Extractor.java @@ -1265,10 +1265,17 @@ public class FragmentedMp4Extractor implements Extractor { 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; @@ -1555,9 +1562,13 @@ public class FragmentedMp4Extractor implements Extractor { /** * 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; @@ -1576,24 +1587,66 @@ public class FragmentedMp4Extractor implements Extractor { 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; } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/SampleDataQueue.java b/library/core/src/main/java/com/google/android/exoplayer2/source/SampleDataQueue.java index 26a95b8de2..68761cef19 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/source/SampleDataQueue.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/SampleDataQueue.java @@ -23,7 +23,6 @@ import com.google.android.exoplayer2.extractor.TrackOutput.CryptoData; import com.google.android.exoplayer2.source.SampleQueue.SampleExtrasHolder; import com.google.android.exoplayer2.upstream.Allocation; import com.google.android.exoplayer2.upstream.Allocator; -import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.ParsableByteArray; import java.io.EOFException; import java.io.IOException; @@ -115,13 +114,11 @@ import java.nio.ByteBuffer; * * @param buffer The buffer to populate. * @param extrasHolder The extras holder whose offset should be read and subsequently adjusted. - * @param mimeType The MIME type. */ - public void readToBuffer(DecoderInputBuffer buffer, SampleExtrasHolder extrasHolder, - String mimeType) { + public void readToBuffer(DecoderInputBuffer buffer, SampleExtrasHolder extrasHolder) { // Read encryption data if the sample is encrypted. if (buffer.isEncrypted()) { - readEncryptionData(buffer, extrasHolder, mimeType); + readEncryptionData(buffer, extrasHolder); } // Read sample data, extracting supplemental data into a separate buffer if needed. if (buffer.hasSupplementalData()) { @@ -218,10 +215,8 @@ import java.nio.ByteBuffer; * * @param buffer The buffer into which the encryption data should be written. * @param extrasHolder The extras holder whose offset should be read and subsequently adjusted. - * @param mimeType The MIME type. */ - private void readEncryptionData(DecoderInputBuffer buffer, SampleExtrasHolder extrasHolder, - String mimeType) { + private void readEncryptionData(DecoderInputBuffer buffer, SampleExtrasHolder extrasHolder) { long offset = extrasHolder.offset; // Read the signal byte. @@ -270,10 +265,8 @@ import java.nio.ByteBuffer; encryptedDataSizes[i] = scratch.readUnsignedIntToInt(); } } else { - int addedHeaderSize = MimeTypes.AUDIO_AC4.equals(mimeType) ? 7 : 0; - clearDataSizes[0] = addedHeaderSize; - encryptedDataSizes[0] = extrasHolder.size - (int) (offset - extrasHolder.offset) - - addedHeaderSize; + clearDataSizes[0] = 0; + encryptedDataSizes[0] = extrasHolder.size - (int) (offset - extrasHolder.offset); } // Populate the cryptoInfo. diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/SampleQueue.java b/library/core/src/main/java/com/google/android/exoplayer2/source/SampleQueue.java index bfd3d7c4ed..cc15d9d275 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/source/SampleQueue.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/SampleQueue.java @@ -323,8 +323,7 @@ public class SampleQueue implements TrackOutput { readSampleMetadata( formatHolder, buffer, formatRequired, loadingFinished, decodeOnlyUntilUs, extrasHolder); if (result == C.RESULT_BUFFER_READ && !buffer.isEndOfStream() && !buffer.isFlagsOnly()) { - sampleDataQueue.readToBuffer(buffer, extrasHolder, - downstreamFormat == null ? null : downstreamFormat.sampleMimeType); + sampleDataQueue.readToBuffer(buffer, extrasHolder); } return result; }