diff --git a/extensions/flac/src/main/java/com/google/android/exoplayer/ext/flac/FlacExtractor.java b/extensions/flac/src/main/java/com/google/android/exoplayer/ext/flac/FlacExtractor.java index 19820362e0..b30c4aa4ae 100644 --- a/extensions/flac/src/main/java/com/google/android/exoplayer/ext/flac/FlacExtractor.java +++ b/extensions/flac/src/main/java/com/google/android/exoplayer/ext/flac/FlacExtractor.java @@ -105,8 +105,8 @@ public final class FlacExtractor implements Extractor { }); Format mediaFormat = Format.createAudioSampleFormat(null, MimeTypes.AUDIO_RAW, - streamInfo.bitRate(), Format.NO_VALUE, - streamInfo.channels, streamInfo.sampleRate, null, null); + streamInfo.bitRate(), Format.NO_VALUE, streamInfo.channels, streamInfo.sampleRate, null, + null, null); trackOutput.format(mediaFormat); outputBuffer = new ParsableByteArray(streamInfo.maxDecodedFrameSize()); diff --git a/library/src/androidTest/java/com/google/android/exoplayer/FormatTest.java b/library/src/androidTest/java/com/google/android/exoplayer/FormatTest.java index 1ea17302ec..ec9df17934 100644 --- a/library/src/androidTest/java/com/google/android/exoplayer/FormatTest.java +++ b/library/src/androidTest/java/com/google/android/exoplayer/FormatTest.java @@ -46,17 +46,17 @@ public final class FormatTest extends TestCase { initData.add(initData2); testConversionToFrameworkMediaFormatV16(Format.createVideoSampleFormat( - null, "video/xyz", 5000, 102400, 1280, 720, 30, initData)); + null, "video/xyz", 5000, 102400, 1280, 720, 30, initData, null)); testConversionToFrameworkMediaFormatV16(Format.createVideoSampleFormat( - null, "video/xyz", 5000, Format.NO_VALUE, 1280, 720, 30, null)); + null, "video/xyz", 5000, Format.NO_VALUE, 1280, 720, 30, null, null)); testConversionToFrameworkMediaFormatV16(Format.createAudioSampleFormat( - null, "audio/xyz", 500, 128, 5, 44100, initData, null)); + null, "audio/xyz", 500, 128, 5, 44100, initData, null, null)); testConversionToFrameworkMediaFormatV16(Format.createAudioSampleFormat( - null, "audio/xyz", 500, Format.NO_VALUE, 5, 44100, null, null)); + null, "audio/xyz", 500, Format.NO_VALUE, 5, 44100, null, null, null)); testConversionToFrameworkMediaFormatV16( - Format.createTextSampleFormat(null, "text/xyz", Format.NO_VALUE, "eng")); + Format.createTextSampleFormat(null, "text/xyz", Format.NO_VALUE, "eng", null)); testConversionToFrameworkMediaFormatV16( - Format.createTextSampleFormat(null, "text/xyz", Format.NO_VALUE, null)); + Format.createTextSampleFormat(null, "text/xyz", Format.NO_VALUE, null, null)); } @SuppressLint("InlinedApi") diff --git a/library/src/androidTest/java/com/google/android/exoplayer/extractor/mkv/MatroskaExtractorTest.java b/library/src/androidTest/java/com/google/android/exoplayer/extractor/mkv/MatroskaExtractorTest.java index 227e5886c6..35319f39f3 100644 --- a/library/src/androidTest/java/com/google/android/exoplayer/extractor/mkv/MatroskaExtractorTest.java +++ b/library/src/androidTest/java/com/google/android/exoplayer/extractor/mkv/MatroskaExtractorTest.java @@ -67,7 +67,6 @@ public final class MatroskaExtractorTest extends InstrumentationTestCase { private static final byte SECOND_AUDIO_TRACK_NUMBER = 0x05; private static final UUID WIDEVINE_UUID = new UUID(0xEDEF8BA979D64ACEL, 0xA3C827DCD51D21EDL); - private static final UUID ZERO_UUID = new UUID(0, 0); private static final String MATROSKA_DOC_TYPE = "matroska"; private static final String WEBM_DOC_TYPE = "webm"; @@ -234,15 +233,8 @@ public final class MatroskaExtractorTest extends InstrumentationTestCase { assertTracksEnded(); assertVp9VideoFormat(VIDEO_TRACK_NUMBER); + assertDrmInitData(VIDEO_TRACK_NUMBER); assertSeekMap(DEFAULT_TIMECODE_SCALE, 1); - DrmInitData drmInitData = extractorOutput.drmInitData; - assertNotNull(drmInitData); - SchemeData widevineInitData = drmInitData.get(WIDEVINE_UUID); - assertEquals(MimeTypes.VIDEO_WEBM, widevineInitData.mimeType); - android.test.MoreAsserts.assertEquals(TEST_ENCRYPTION_KEY_ID, widevineInitData.data); - SchemeData zeroInitData = drmInitData.get(ZERO_UUID); - assertEquals(MimeTypes.VIDEO_WEBM, zeroInitData.mimeType); - android.test.MoreAsserts.assertEquals(TEST_ENCRYPTION_KEY_ID, zeroInitData.data); } public void testPrepareThreeCuePoints() throws IOException, InterruptedException { @@ -755,6 +747,17 @@ public final class MatroskaExtractorTest extends InstrumentationTestCase { } } + private void assertDrmInitData(int trackNumber) { + DrmInitData drmInitData = getTrackOutput(trackNumber).format.drmInitData; + assertNotNull(drmInitData); + SchemeData widevineInitData = drmInitData.get(WIDEVINE_UUID); + assertEquals(MimeTypes.VIDEO_WEBM, widevineInitData.mimeType); + android.test.MoreAsserts.assertEquals(TEST_ENCRYPTION_KEY_ID, widevineInitData.data); + SchemeData zeroInitData = drmInitData.get(C.UUID_NIL); + assertEquals(MimeTypes.VIDEO_WEBM, zeroInitData.mimeType); + android.test.MoreAsserts.assertEquals(TEST_ENCRYPTION_KEY_ID, zeroInitData.data); + } + private void assertSeekMap(int timecodeScale, int cuePointCount) { ChunkIndex index = (ChunkIndex) extractorOutput.seekMap; assertEquals(cuePointCount, index.length); diff --git a/library/src/androidTest/java/com/google/android/exoplayer/testutil/FakeExtractorOutput.java b/library/src/androidTest/java/com/google/android/exoplayer/testutil/FakeExtractorOutput.java index d8aa2ed019..c15b5c5169 100644 --- a/library/src/androidTest/java/com/google/android/exoplayer/testutil/FakeExtractorOutput.java +++ b/library/src/androidTest/java/com/google/android/exoplayer/testutil/FakeExtractorOutput.java @@ -15,7 +15,6 @@ */ package com.google.android.exoplayer.testutil; -import com.google.android.exoplayer.drm.DrmInitData; import com.google.android.exoplayer.extractor.ExtractorOutput; import com.google.android.exoplayer.extractor.SeekMap; @@ -36,7 +35,6 @@ public final class FakeExtractorOutput implements ExtractorOutput { public int numberOfTracks; public boolean tracksEnded; public SeekMap seekMap; - public DrmInitData drmInitData; public FakeExtractorOutput() { this(false); @@ -70,11 +68,6 @@ public final class FakeExtractorOutput implements ExtractorOutput { this.seekMap = seekMap; } - @Override - public void drmInitData(DrmInitData drmInitData) { - this.drmInitData = drmInitData; - } - public void assertEquals(FakeExtractorOutput expected) { Assert.assertEquals(expected.numberOfTracks, numberOfTracks); Assert.assertEquals(expected.tracksEnded, tracksEnded); @@ -87,13 +80,6 @@ public final class FakeExtractorOutput implements ExtractorOutput { Assert.assertEquals(expected.seekMap.isSeekable(), seekMap.isSeekable()); Assert.assertEquals(expected.seekMap.getPosition(0), seekMap.getPosition(0)); } - if (expected.drmInitData == null) { - Assert.assertNull(drmInitData); - } else { - // TODO: Bulk up this check if possible. - Assert.assertNotNull(drmInitData); - Assert.assertEquals(expected.drmInitData.getClass(), drmInitData.getClass()); - } for (int i = 0; i < numberOfTracks; i++) { Assert.assertEquals(expected.trackOutputs.keyAt(i), trackOutputs.keyAt(i)); trackOutputs.valueAt(i).assertEquals(expected.trackOutputs.valueAt(i)); diff --git a/library/src/main/java/com/google/android/exoplayer/Format.java b/library/src/main/java/com/google/android/exoplayer/Format.java index eaa16be7d8..aecf065853 100644 --- a/library/src/main/java/com/google/android/exoplayer/Format.java +++ b/library/src/main/java/com/google/android/exoplayer/Format.java @@ -15,6 +15,7 @@ */ package com.google.android.exoplayer; +import com.google.android.exoplayer.drm.DrmInitData; import com.google.android.exoplayer.util.Util; import android.annotation.SuppressLint; @@ -89,6 +90,10 @@ public final class Format { * if initialization data is not required. */ public final List initializationData; + /** + * DRM initialization data if the stream is protected, or null otherwise. + */ + public final DrmInitData drmInitData; // Video specific. @@ -163,21 +168,22 @@ public final class Format { List initializationData) { return new Format(id, containerMimeType, sampleMimeType, bitrate, NO_VALUE, width, height, frameRate, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, null, - OFFSET_SAMPLE_RELATIVE, initializationData, false); - } - - public static Format createVideoSampleFormat(String id, String sampleMimeType, int bitrate, - int maxInputSize, int width, int height, float frameRate, List initializationData) { - return createVideoSampleFormat(id, sampleMimeType, bitrate, maxInputSize, width, height, - frameRate, initializationData, NO_VALUE, NO_VALUE); + OFFSET_SAMPLE_RELATIVE, initializationData, null, false); } public static Format createVideoSampleFormat(String id, String sampleMimeType, int bitrate, int maxInputSize, int width, int height, float frameRate, List initializationData, - int rotationDegrees, float pixelWidthHeightRatio) { + DrmInitData drmInitData) { + return createVideoSampleFormat(id, sampleMimeType, bitrate, maxInputSize, width, height, + frameRate, initializationData, NO_VALUE, NO_VALUE, drmInitData); + } + + public static Format createVideoSampleFormat(String id, String sampleMimeType, int bitrate, + int maxInputSize, int width, int height, float frameRate, List initializationData, + int rotationDegrees, float pixelWidthHeightRatio, DrmInitData drmInitData) { return new Format(id, null, sampleMimeType, bitrate, maxInputSize, width, height, frameRate, rotationDegrees, pixelWidthHeightRatio, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, null, - OFFSET_SAMPLE_RELATIVE, initializationData, false); + OFFSET_SAMPLE_RELATIVE, initializationData, drmInitData, false); } // Audio. @@ -187,22 +193,22 @@ public final class Format { List initializationData, String language) { return new Format(id, containerMimeType, sampleMimeType, bitrate, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, channelCount, sampleRate, NO_VALUE, NO_VALUE, language, - OFFSET_SAMPLE_RELATIVE, initializationData, false); + OFFSET_SAMPLE_RELATIVE, initializationData, null, false); } public static Format createAudioSampleFormat(String id, String sampleMimeType, int bitrate, int maxInputSize, int channelCount, int sampleRate, List initializationData, - String language) { + String language, DrmInitData drmInitData) { return createAudioSampleFormat(id, sampleMimeType, bitrate, maxInputSize, channelCount, - sampleRate, NO_VALUE, NO_VALUE, initializationData, language); + sampleRate, NO_VALUE, NO_VALUE, initializationData, language, drmInitData); } public static Format createAudioSampleFormat(String id, String sampleMimeType, int bitrate, int maxInputSize, int channelCount, int sampleRate, int encoderDelay, int encoderPadding, - List initializationData, String language) { + List initializationData, String language, DrmInitData drmInitData) { return new Format(id, null, sampleMimeType, bitrate, maxInputSize, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, channelCount, sampleRate, encoderDelay, encoderPadding, language, - OFFSET_SAMPLE_RELATIVE, initializationData, false); + OFFSET_SAMPLE_RELATIVE, initializationData, drmInitData, false); } // Text. @@ -211,28 +217,29 @@ public final class Format { String sampleMimeType, int bitrate, String language) { return new Format(id, containerMimeType, sampleMimeType, bitrate, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, language, - OFFSET_SAMPLE_RELATIVE, null, false); + OFFSET_SAMPLE_RELATIVE, null, null, false); } public static Format createTextSampleFormat(String id, String sampleMimeType, int bitrate, - String language) { - return createTextSampleFormat(id, sampleMimeType, bitrate, language, OFFSET_SAMPLE_RELATIVE); + String language, DrmInitData drmInitData) { + return createTextSampleFormat(id, sampleMimeType, bitrate, language, drmInitData, + OFFSET_SAMPLE_RELATIVE); } public static Format createTextSampleFormat(String id, String sampleMimeType, int bitrate, - String language, long subsampleOffsetUs) { + String language, DrmInitData drmInitData, long subsampleOffsetUs) { return new Format(id, null, sampleMimeType, bitrate, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, language, - subsampleOffsetUs, null, false); + subsampleOffsetUs, null, drmInitData, false); } // Image. public static Format createImageSampleFormat(String id, String sampleMimeType, int bitrate, - List initializationData, String language) { + List initializationData, String language, DrmInitData drmInitData) { return new Format(id, null, sampleMimeType, bitrate, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, language, - OFFSET_SAMPLE_RELATIVE, initializationData, false); + OFFSET_SAMPLE_RELATIVE, initializationData, drmInitData, false); } // Generic. @@ -241,20 +248,21 @@ public final class Format { String sampleMimeType, int bitrate) { return new Format(id, containerMimeType, sampleMimeType, bitrate, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, null, - OFFSET_SAMPLE_RELATIVE, null, false); + OFFSET_SAMPLE_RELATIVE, null, null, false); } - public static Format createSampleFormat(String id, String sampleMimeType, int bitrate) { + public static Format createSampleFormat(String id, String sampleMimeType, int bitrate, + DrmInitData drmInitData) { return new Format(id, null, sampleMimeType, bitrate, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, null, OFFSET_SAMPLE_RELATIVE, - null, false); + null, drmInitData, false); } /* package */ Format(String id, String containerMimeType, String sampleMimeType, int bitrate, int maxInputSize, int width, int height, float frameRate, int rotationDegrees, float pixelWidthHeightRatio, int channelCount, int sampleRate, int encoderDelay, int encoderPadding, String language, long subsampleOffsetUs, List initializationData, - boolean requiresSecureDecryption) { + DrmInitData drmInitData, boolean requiresSecureDecryption) { this.id = id; this.containerMimeType = containerMimeType; this.sampleMimeType = sampleMimeType; @@ -273,6 +281,7 @@ public final class Format { this.subsampleOffsetUs = subsampleOffsetUs; this.initializationData = initializationData == null ? Collections.emptyList() : initializationData; + this.drmInitData = drmInitData; this.requiresSecureDecryption = requiresSecureDecryption; } @@ -280,14 +289,14 @@ public final class Format { return new Format(id, containerMimeType, sampleMimeType, bitrate, maxInputSize, width, height, frameRate, rotationDegrees, pixelWidthHeightRatio, channelCount, sampleRate, encoderDelay, encoderPadding, language, subsampleOffsetUs, initializationData, - requiresSecureDecryption); + drmInitData, requiresSecureDecryption); } public Format copyWithSubsampleOffsetUs(long subsampleOffsetUs) { return new Format(id, containerMimeType, sampleMimeType, bitrate, maxInputSize, width, height, frameRate, rotationDegrees, pixelWidthHeightRatio, channelCount, sampleRate, encoderDelay, encoderPadding, language, subsampleOffsetUs, initializationData, - requiresSecureDecryption); + drmInitData, requiresSecureDecryption); } public Format copyWithContainerInfo(String id, int bitrate, int width, int height, @@ -295,14 +304,21 @@ public final class Format { return new Format(id, containerMimeType, sampleMimeType, bitrate, maxInputSize, width, height, frameRate, rotationDegrees, pixelWidthHeightRatio, channelCount, sampleRate, encoderDelay, encoderPadding, language, subsampleOffsetUs, initializationData, - requiresSecureDecryption); + drmInitData, requiresSecureDecryption); } public Format copyWithGaplessInfo(int encoderDelay, int encoderPadding) { return new Format(id, containerMimeType, sampleMimeType, bitrate, maxInputSize, width, height, frameRate, rotationDegrees, pixelWidthHeightRatio, channelCount, sampleRate, encoderDelay, encoderPadding, language, subsampleOffsetUs, initializationData, - requiresSecureDecryption); + drmInitData, requiresSecureDecryption); + } + + public Format copyWithDrmInitData(DrmInitData drmInitData) { + return new Format(id, containerMimeType, sampleMimeType, bitrate, maxInputSize, width, + height, frameRate, rotationDegrees, pixelWidthHeightRatio, channelCount, sampleRate, + encoderDelay, encoderPadding, language, subsampleOffsetUs, initializationData, + drmInitData, requiresSecureDecryption); } /** @@ -366,6 +382,7 @@ public final class Format { result = 31 * result + encoderDelay; result = 31 * result + encoderPadding; result = 31 * result + (language == null ? 0 : language.hashCode()); + result = 31 * result + (drmInitData == null ? 0 : drmInitData.hashCode()); hashCode = result; } return hashCode; @@ -391,6 +408,7 @@ public final class Format { || !Util.areEqual(id, other.id) || !Util.areEqual(language, other.language) || !Util.areEqual(containerMimeType, other.containerMimeType) || !Util.areEqual(sampleMimeType, other.sampleMimeType) + || !Util.areEqual(drmInitData, other.drmInitData) || initializationData.size() != other.initializationData.size()) { return false; } diff --git a/library/src/main/java/com/google/android/exoplayer/FormatHolder.java b/library/src/main/java/com/google/android/exoplayer/FormatHolder.java index 63bb2163e1..92fb05d7cd 100644 --- a/library/src/main/java/com/google/android/exoplayer/FormatHolder.java +++ b/library/src/main/java/com/google/android/exoplayer/FormatHolder.java @@ -15,8 +15,6 @@ */ package com.google.android.exoplayer; -import com.google.android.exoplayer.drm.DrmInitData; - /** * Holds a {@link Format} and corresponding drm scheme initialization data. */ @@ -26,9 +24,5 @@ public final class FormatHolder { * The format of the media. */ public Format format; - /** - * Initialization data for drm schemes supported by the media. Null if the media is not encrypted. - */ - public DrmInitData drmInitData; } diff --git a/library/src/main/java/com/google/android/exoplayer/FrameworkSampleSource.java b/library/src/main/java/com/google/android/exoplayer/FrameworkSampleSource.java index 9d68157eb4..515373ab0d 100644 --- a/library/src/main/java/com/google/android/exoplayer/FrameworkSampleSource.java +++ b/library/src/main/java/com/google/android/exoplayer/FrameworkSampleSource.java @@ -136,12 +136,13 @@ public final class FrameworkSampleSource implements SampleSource { } trackStates = new int[extractor.getTrackCount()]; TrackGroup[] trackArray = new TrackGroup[trackStates.length]; + DrmInitData drmInitData = Util.SDK_INT >= 18 ? getDrmInitDataV18() : null; for (int i = 0; i < trackStates.length; i++) { MediaFormat format = extractor.getTrackFormat(i); if (format.containsKey(MediaFormat.KEY_DURATION)) { durationUs = Math.max(durationUs, format.getLong(MediaFormat.KEY_DURATION)); } - trackArray[i] = new TrackGroup(createFormat(i, format)); + trackArray[i] = new TrackGroup(createFormat(i, format, drmInitData)); } tracks = new TrackGroupArray(trackArray); prepared = true; @@ -244,7 +245,6 @@ public final class FrameworkSampleSource implements SampleSource { } if (trackStates[track] != TRACK_STATE_FORMAT_SENT) { formatHolder.format = tracks.get(track).getFormat(0); - formatHolder.drmInitData = Util.SDK_INT >= 18 ? getDrmInitDataV18() : null; trackStates[track] = TRACK_STATE_FORMAT_SENT; return TrackStream.FORMAT_READ; } @@ -307,7 +307,7 @@ public final class FrameworkSampleSource implements SampleSource { } @SuppressLint("InlinedApi") - private static Format createFormat(int index, MediaFormat mediaFormat) { + private static Format createFormat(int index, MediaFormat mediaFormat, DrmInitData drmInitData) { String mimeType = mediaFormat.getString(MediaFormat.KEY_MIME); String language = getOptionalStringV16(mediaFormat, MediaFormat.KEY_LANGUAGE); int maxInputSize = getOptionalIntegerV16(mediaFormat, MediaFormat.KEY_MAX_INPUT_SIZE); @@ -336,7 +336,7 @@ public final class FrameworkSampleSource implements SampleSource { Format format = new Format(Integer.toString(index), null, mimeType, Format.NO_VALUE, maxInputSize, width, height, frameRate, rotationDegrees, Format.NO_VALUE, channelCount, sampleRate, encoderDelay, encoderPadding, language, Format.OFFSET_SAMPLE_RELATIVE, - initializationData, false); + initializationData, drmInitData, false); format.setFrameworkMediaFormatV16(mediaFormat); return format; } diff --git a/library/src/main/java/com/google/android/exoplayer/MediaCodecTrackRenderer.java b/library/src/main/java/com/google/android/exoplayer/MediaCodecTrackRenderer.java index 0d29bf854e..3bd13b2647 100644 --- a/library/src/main/java/com/google/android/exoplayer/MediaCodecTrackRenderer.java +++ b/library/src/main/java/com/google/android/exoplayer/MediaCodecTrackRenderer.java @@ -16,7 +16,6 @@ package com.google.android.exoplayer; import com.google.android.exoplayer.MediaCodecUtil.DecoderQueryException; -import com.google.android.exoplayer.drm.DrmInitData; import com.google.android.exoplayer.drm.DrmSessionManager; import com.google.android.exoplayer.util.Assertions; import com.google.android.exoplayer.util.NalUnitUtil; @@ -171,7 +170,6 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer { protected final Handler eventHandler; private Format format; - private DrmInitData drmInitData; private MediaCodec codec; private boolean codecIsAdaptive; private boolean codecNeedsDiscardToSpsWorkaround; @@ -286,13 +284,13 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer { String mimeType = format.sampleMimeType; MediaCrypto mediaCrypto = null; boolean requiresSecureDecoder = false; - if (drmInitData != null) { + if (format.drmInitData != null) { if (drmSessionManager == null) { throw ExoPlaybackException.createForRenderer( new IllegalStateException("Media requires a DrmSessionManager"), getIndex()); } if (!openedDrmSession) { - drmSessionManager.open(drmInitData); + drmSessionManager.open(format.drmInitData); openedDrmSession = true; } int drmSessionState = drmSessionManager.getState(); @@ -376,7 +374,6 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer { @Override protected void onDisabled() { format = null; - drmInitData = null; try { releaseCodec(); } finally { @@ -665,7 +662,6 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer { protected void onInputFormatChanged(FormatHolder formatHolder) throws ExoPlaybackException { Format oldFormat = format; format = formatHolder.format; - drmInitData = formatHolder.drmInitData; if (codec != null && canReconfigureCodec(codec, codecIsAdaptive, oldFormat, format)) { codecReconfigured = true; codecReconfigurationState = RECONFIGURATION_STATE_WRITE_PENDING; diff --git a/library/src/main/java/com/google/android/exoplayer/chunk/BaseMediaChunk.java b/library/src/main/java/com/google/android/exoplayer/chunk/BaseMediaChunk.java index 3483ed68d5..162360c156 100644 --- a/library/src/main/java/com/google/android/exoplayer/chunk/BaseMediaChunk.java +++ b/library/src/main/java/com/google/android/exoplayer/chunk/BaseMediaChunk.java @@ -16,7 +16,6 @@ package com.google.android.exoplayer.chunk; import com.google.android.exoplayer.Format; -import com.google.android.exoplayer.drm.DrmInitData; import com.google.android.exoplayer.extractor.DefaultTrackOutput; import com.google.android.exoplayer.upstream.DataSource; import com.google.android.exoplayer.upstream.DataSpec; @@ -64,13 +63,6 @@ public abstract class BaseMediaChunk extends MediaChunk { return firstSampleIndex; } - /** - * Gets the {@link DrmInitData} corresponding to the chunk. - * - * @return The {@link DrmInitData} corresponding to this chunk. - */ - public abstract DrmInitData getDrmInitData(); - /** * Returns the track output most recently passed to {@link #init(DefaultTrackOutput)}. */ diff --git a/library/src/main/java/com/google/android/exoplayer/chunk/ChunkExtractorWrapper.java b/library/src/main/java/com/google/android/exoplayer/chunk/ChunkExtractorWrapper.java index 63850a16bc..e1a4223d80 100644 --- a/library/src/main/java/com/google/android/exoplayer/chunk/ChunkExtractorWrapper.java +++ b/library/src/main/java/com/google/android/exoplayer/chunk/ChunkExtractorWrapper.java @@ -45,14 +45,11 @@ public final class ChunkExtractorWrapper implements ExtractorOutput, TrackOutput */ void seekMap(SeekMap seekMap); - /** - * @see ExtractorOutput#drmInitData(DrmInitData) - */ - void drmInitData(DrmInitData drmInitData); - } private final Extractor extractor; + private final DrmInitData drmInitData; + private boolean extractorInitialized; private SingleTrackMetadataOutput metadataOutput; private TrackOutput trackOutput; @@ -62,9 +59,12 @@ public final class ChunkExtractorWrapper implements ExtractorOutput, TrackOutput /** * @param extractor The extractor to wrap. + * @param drmInitData {@link DrmInitData} that should be added to any format extracted from the + * stream. If set, overrides any {@link DrmInitData} extracted from the stream. */ - public ChunkExtractorWrapper(Extractor extractor) { + public ChunkExtractorWrapper(Extractor extractor, DrmInitData drmInitData) { this.extractor = extractor; + this.drmInitData = drmInitData; } /** @@ -118,15 +118,13 @@ public final class ChunkExtractorWrapper implements ExtractorOutput, TrackOutput metadataOutput.seekMap(seekMap); } - @Override - public void drmInitData(DrmInitData drmInitData) { - metadataOutput.drmInitData(drmInitData); - } - // TrackOutput implementation. @Override public void format(Format format) { + if (drmInitData != null) { + format = format.copyWithDrmInitData(drmInitData); + } trackOutput.format(format); } diff --git a/library/src/main/java/com/google/android/exoplayer/chunk/ChunkTrackStream.java b/library/src/main/java/com/google/android/exoplayer/chunk/ChunkTrackStream.java index 462e00f0bd..20347fd3ee 100644 --- a/library/src/main/java/com/google/android/exoplayer/chunk/ChunkTrackStream.java +++ b/library/src/main/java/com/google/android/exoplayer/chunk/ChunkTrackStream.java @@ -226,11 +226,7 @@ public class ChunkTrackStream implements TrackStream, Loader.Callback { } downstreamFormat = format; - int result = sampleQueue.readData(formatHolder, buffer, loadingFinished, lastSeekPositionUs); - if (result == FORMAT_READ) { - formatHolder.drmInitData = currentChunk.getDrmInitData(); - } - return result; + return sampleQueue.readData(formatHolder, buffer, loadingFinished, lastSeekPositionUs); } // Loader.Callback implementation. diff --git a/library/src/main/java/com/google/android/exoplayer/chunk/ContainerMediaChunk.java b/library/src/main/java/com/google/android/exoplayer/chunk/ContainerMediaChunk.java index 281632ea1e..2beefce83c 100644 --- a/library/src/main/java/com/google/android/exoplayer/chunk/ContainerMediaChunk.java +++ b/library/src/main/java/com/google/android/exoplayer/chunk/ContainerMediaChunk.java @@ -17,7 +17,6 @@ package com.google.android.exoplayer.chunk; import com.google.android.exoplayer.Format; import com.google.android.exoplayer.chunk.ChunkExtractorWrapper.SingleTrackMetadataOutput; -import com.google.android.exoplayer.drm.DrmInitData; import com.google.android.exoplayer.extractor.DefaultExtractorInput; import com.google.android.exoplayer.extractor.DefaultTrackOutput; import com.google.android.exoplayer.extractor.Extractor; @@ -38,7 +37,6 @@ public class ContainerMediaChunk extends BaseMediaChunk implements SingleTrackMe private final long sampleOffsetUs; private final Format sampleFormat; - private volatile DrmInitData drmInitData; private volatile int bytesLoaded; private volatile boolean loadCanceled; @@ -54,17 +52,14 @@ public class ContainerMediaChunk extends BaseMediaChunk implements SingleTrackMe * @param extractorWrapper A wrapped extractor to use for parsing the data. * @param sampleFormat The {@link Format} of the samples in the chunk, if known. May be null if * the data is known to define its own sample format. - * @param drmInitData The {@link DrmInitData} for the chunk. Null if the media is not drm - * protected. May also be null if the data is known to define its own initialization data. */ public ContainerMediaChunk(DataSource dataSource, DataSpec dataSpec, int trigger, Format format, long startTimeUs, long endTimeUs, int chunkIndex, long sampleOffsetUs, - ChunkExtractorWrapper extractorWrapper, Format sampleFormat, DrmInitData drmInitData) { + ChunkExtractorWrapper extractorWrapper, Format sampleFormat) { super(dataSource, dataSpec, trigger, format, startTimeUs, endTimeUs, chunkIndex); this.extractorWrapper = extractorWrapper; this.sampleOffsetUs = sampleOffsetUs; this.sampleFormat = sampleFormat; - this.drmInitData = drmInitData; } @Override @@ -72,11 +67,6 @@ public class ContainerMediaChunk extends BaseMediaChunk implements SingleTrackMe return bytesLoaded; } - @Override - public final DrmInitData getDrmInitData() { - return drmInitData; - } - // SingleTrackMetadataOutput implementation. @Override @@ -84,11 +74,6 @@ public class ContainerMediaChunk extends BaseMediaChunk implements SingleTrackMe // Do nothing. } - @Override - public final void drmInitData(DrmInitData drmInitData) { - this.drmInitData = drmInitData; - } - // Loadable implementation. @Override diff --git a/library/src/main/java/com/google/android/exoplayer/chunk/InitializationChunk.java b/library/src/main/java/com/google/android/exoplayer/chunk/InitializationChunk.java index 303419617d..18791d46ea 100644 --- a/library/src/main/java/com/google/android/exoplayer/chunk/InitializationChunk.java +++ b/library/src/main/java/com/google/android/exoplayer/chunk/InitializationChunk.java @@ -17,7 +17,6 @@ package com.google.android.exoplayer.chunk; import com.google.android.exoplayer.Format; import com.google.android.exoplayer.chunk.ChunkExtractorWrapper.SingleTrackMetadataOutput; -import com.google.android.exoplayer.drm.DrmInitData; import com.google.android.exoplayer.extractor.DefaultExtractorInput; import com.google.android.exoplayer.extractor.Extractor; import com.google.android.exoplayer.extractor.ExtractorInput; @@ -42,7 +41,6 @@ public final class InitializationChunk extends Chunk implements SingleTrackMetad // has completed. These variables do not need to be volatile, since a memory barrier must occur // for the reading thread to know that loading has completed. private Format sampleFormat; - private DrmInitData drmInitData; private SeekMap seekMap; private volatile int bytesLoaded; @@ -77,15 +75,6 @@ public final class InitializationChunk extends Chunk implements SingleTrackMetad return sampleFormat; } - /** - * Returns a {@link DrmInitData} parsed from the chunk, or null. - *

- * Should be called after loading has completed. - */ - public DrmInitData getDrmInitData() { - return drmInitData; - } - /** * Returns a {@link SeekMap} parsed from the chunk, or null. *

@@ -102,11 +91,6 @@ public final class InitializationChunk extends Chunk implements SingleTrackMetad this.seekMap = seekMap; } - @Override - public void drmInitData(DrmInitData drmInitData) { - this.drmInitData = drmInitData; - } - // TrackOutput implementation. @Override diff --git a/library/src/main/java/com/google/android/exoplayer/chunk/SingleSampleMediaChunk.java b/library/src/main/java/com/google/android/exoplayer/chunk/SingleSampleMediaChunk.java index 82a3dc8696..9bdf071431 100644 --- a/library/src/main/java/com/google/android/exoplayer/chunk/SingleSampleMediaChunk.java +++ b/library/src/main/java/com/google/android/exoplayer/chunk/SingleSampleMediaChunk.java @@ -17,7 +17,6 @@ package com.google.android.exoplayer.chunk; import com.google.android.exoplayer.C; import com.google.android.exoplayer.Format; -import com.google.android.exoplayer.drm.DrmInitData; import com.google.android.exoplayer.extractor.DefaultExtractorInput; import com.google.android.exoplayer.extractor.DefaultTrackOutput; import com.google.android.exoplayer.extractor.ExtractorInput; @@ -33,7 +32,6 @@ import java.io.IOException; public final class SingleSampleMediaChunk extends BaseMediaChunk { private final Format sampleFormat; - private final DrmInitData sampleDrmInitData; private volatile int bytesLoaded; private volatile boolean loadCanceled; @@ -47,15 +45,11 @@ public final class SingleSampleMediaChunk extends BaseMediaChunk { * @param endTimeUs The end time of the media contained by the chunk, in microseconds. * @param chunkIndex The index of the chunk. * @param sampleFormat The format of the sample. - * @param sampleDrmInitData The {@link DrmInitData} for the sample. Null if the sample is not drm - * protected. */ public SingleSampleMediaChunk(DataSource dataSource, DataSpec dataSpec, int trigger, - Format format, long startTimeUs, long endTimeUs, int chunkIndex, Format sampleFormat, - DrmInitData sampleDrmInitData) { + Format format, long startTimeUs, long endTimeUs, int chunkIndex, Format sampleFormat) { super(dataSource, dataSpec, trigger, format, startTimeUs, endTimeUs, chunkIndex); this.sampleFormat = sampleFormat; - this.sampleDrmInitData = sampleDrmInitData; } @Override @@ -63,11 +57,6 @@ public final class SingleSampleMediaChunk extends BaseMediaChunk { return bytesLoaded; } - @Override - public DrmInitData getDrmInitData() { - return sampleDrmInitData; - } - // Loadable implementation. @Override diff --git a/library/src/main/java/com/google/android/exoplayer/dash/DashChunkSource.java b/library/src/main/java/com/google/android/exoplayer/dash/DashChunkSource.java index 7614a6e1c7..7ed8a46e4d 100644 --- a/library/src/main/java/com/google/android/exoplayer/dash/DashChunkSource.java +++ b/library/src/main/java/com/google/android/exoplayer/dash/DashChunkSource.java @@ -31,13 +31,11 @@ import com.google.android.exoplayer.chunk.InitializationChunk; import com.google.android.exoplayer.chunk.MediaChunk; import com.google.android.exoplayer.chunk.SingleSampleMediaChunk; import com.google.android.exoplayer.dash.mpd.AdaptationSet; -import com.google.android.exoplayer.dash.mpd.ContentProtection; import com.google.android.exoplayer.dash.mpd.MediaPresentationDescription; import com.google.android.exoplayer.dash.mpd.Period; import com.google.android.exoplayer.dash.mpd.RangedUri; import com.google.android.exoplayer.dash.mpd.Representation; import com.google.android.exoplayer.drm.DrmInitData; -import com.google.android.exoplayer.drm.DrmInitData.SchemeData; import com.google.android.exoplayer.extractor.ChunkIndex; import com.google.android.exoplayer.extractor.SeekMap; import com.google.android.exoplayer.extractor.mkv.MatroskaExtractor; @@ -50,7 +48,6 @@ import com.google.android.exoplayer.util.Util; import android.os.SystemClock; import java.io.IOException; -import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -70,7 +67,6 @@ public class DashChunkSource implements ChunkSource { private final Evaluation evaluation; private MediaPresentationDescription manifest; - private DrmInitData drmInitData; private boolean lastChunkWasInitialization; private IOException fatalError; @@ -100,7 +96,6 @@ public class DashChunkSource implements ChunkSource { Period period = manifest.getPeriod(0); long periodDurationUs = getPeriodDurationUs(manifest, 0); AdaptationSet adaptationSet = period.adaptationSets.get(adaptationSetIndex); - drmInitData = getDrmInitData(adaptationSet); List representations = adaptationSet.representations; representationHolders = new RepresentationHolder[representations.size()]; @@ -257,7 +252,7 @@ public class DashChunkSource implements ChunkSource { representationHolders[getTrackIndex(initializationChunk.format)]; Format sampleFormat = initializationChunk.getSampleFormat(); if (sampleFormat != null) { - representationHolder.sampleFormat = sampleFormat; + representationHolder.setSampleFormat(sampleFormat); } // The null check avoids overwriting an index obtained from the manifest with one obtained // from the stream. If the manifest defines an index then the stream shouldn't, but in cases @@ -269,11 +264,6 @@ public class DashChunkSource implements ChunkSource { initializationChunk.dataSpec.uri.toString()); } } - // The null check avoids overwriting drmInitData obtained from the manifest with drmInitData - // obtained from the stream, as per DASH IF Interoperability Recommendations V3.0, 7.5.3. - if (drmInitData == null) { - drmInitData = initializationChunk.getDrmInitData(); - } } } @@ -324,12 +314,12 @@ public class DashChunkSource implements ChunkSource { if (representationHolder.extractorWrapper == null) { return new SingleSampleMediaChunk(dataSource, dataSpec, Chunk.TRIGGER_INITIAL, trackFormat, - startTimeUs, endTimeUs, segmentNum, trackFormat, null); + startTimeUs, endTimeUs, segmentNum, trackFormat); } else { long sampleOffsetUs = -representation.presentationTimeOffsetUs; return new ContainerMediaChunk(dataSource, dataSpec, trigger, trackFormat, startTimeUs, endTimeUs, segmentNum, sampleOffsetUs, representationHolder.extractorWrapper, - sampleFormat, drmInitData); + sampleFormat); } } @@ -343,20 +333,6 @@ public class DashChunkSource implements ChunkSource { throw new IllegalStateException("Invalid format: " + format); } - private static DrmInitData getDrmInitData(AdaptationSet adaptationSet) { - ArrayList schemeDatas = null; - for (int i = 0; i < adaptationSet.contentProtections.size(); i++) { - ContentProtection contentProtection = adaptationSet.contentProtections.get(i); - if (contentProtection.schemeData != null) { - if (schemeDatas == null) { - schemeDatas = new ArrayList(); - } - schemeDatas.add(contentProtection.schemeData); - } - } - return schemeDatas == null ? null : new DrmInitData(schemeDatas); - } - private static long getPeriodDurationUs(MediaPresentationDescription manifest, int index) { long durationMs = manifest.getPeriodDuration(index); if (durationMs == -1) { @@ -385,10 +361,20 @@ public class DashChunkSource implements ChunkSource { String containerMimeType = representation.format.containerMimeType; extractorWrapper = mimeTypeIsRawText(containerMimeType) ? null : new ChunkExtractorWrapper( mimeTypeIsWebm(containerMimeType) ? new MatroskaExtractor() - : new FragmentedMp4Extractor()); + : new FragmentedMp4Extractor(), representation.format.drmInitData); segmentIndex = representation.getIndex(); } + public void setSampleFormat(Format sampleFormat) { + DrmInitData manifestDrmInitData = representation.format.drmInitData; + if (manifestDrmInitData != null) { + // Prefer drmInitData obtained from the manifest over drmInitData obtained from the stream, + // as per DASH IF Interoperability Recommendations V3.0, 7.5.3. + sampleFormat = sampleFormat.copyWithDrmInitData(manifestDrmInitData); + } + this.sampleFormat = sampleFormat; + } + public void updateRepresentation(long newPeriodDurationUs, Representation newRepresentation) throws BehindLiveWindowException{ DashSegmentIndex oldIndex = representation.getIndex(); diff --git a/library/src/main/java/com/google/android/exoplayer/dash/mpd/AdaptationSet.java b/library/src/main/java/com/google/android/exoplayer/dash/mpd/AdaptationSet.java index c3d46dee78..3b8a42f99d 100644 --- a/library/src/main/java/com/google/android/exoplayer/dash/mpd/AdaptationSet.java +++ b/library/src/main/java/com/google/android/exoplayer/dash/mpd/AdaptationSet.java @@ -28,26 +28,11 @@ public class AdaptationSet { public final int type; public final List representations; - public final List contentProtections; - public AdaptationSet(int id, int type, List representations, - List contentProtections) { + public AdaptationSet(int id, int type, List representations) { this.id = id; this.type = type; this.representations = Collections.unmodifiableList(representations); - if (contentProtections == null) { - this.contentProtections = Collections.emptyList(); - } else { - this.contentProtections = Collections.unmodifiableList(contentProtections); - } - } - - public AdaptationSet(int id, int type, List representations) { - this(id, type, representations, null); - } - - public boolean hasContentProtection() { - return !contentProtections.isEmpty(); } } diff --git a/library/src/main/java/com/google/android/exoplayer/dash/mpd/ContentProtection.java b/library/src/main/java/com/google/android/exoplayer/dash/mpd/ContentProtection.java deleted file mode 100644 index 0055f4c52e..0000000000 --- a/library/src/main/java/com/google/android/exoplayer/dash/mpd/ContentProtection.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.google.android.exoplayer.dash.mpd; - -import com.google.android.exoplayer.drm.DrmInitData.SchemeData; -import com.google.android.exoplayer.util.Assertions; -import com.google.android.exoplayer.util.Util; - -/** - * Represents a ContentProtection tag in an AdaptationSet. - */ -public class ContentProtection { - - /** - * Identifies the content protection scheme. - */ - public final String schemeUriId; - - /** - * Protection scheme specific initialization data. May be null. - */ - public final SchemeData schemeData; - - /** - * @param schemeUriId Identifies the content protection scheme. - * @param schemeData Protection scheme specific initialization data. May be null. - */ - public ContentProtection(String schemeUriId, SchemeData schemeData) { - this.schemeUriId = Assertions.checkNotNull(schemeUriId); - this.schemeData = schemeData; - } - - @Override - public boolean equals(Object obj) { - if (!(obj instanceof ContentProtection)) { - return false; - } - if (obj == this) { - return true; - } - - ContentProtection other = (ContentProtection) obj; - return schemeUriId.equals(other.schemeUriId) && Util.areEqual(schemeData, other.schemeData); - } - - @Override - public int hashCode() { - return (31 * schemeUriId.hashCode()) + (schemeData != null ? schemeData.hashCode() : 0); - } - -} diff --git a/library/src/main/java/com/google/android/exoplayer/dash/mpd/MediaPresentationDescriptionParser.java b/library/src/main/java/com/google/android/exoplayer/dash/mpd/MediaPresentationDescriptionParser.java index 926526b095..c74c1fa949 100644 --- a/library/src/main/java/com/google/android/exoplayer/dash/mpd/MediaPresentationDescriptionParser.java +++ b/library/src/main/java/com/google/android/exoplayer/dash/mpd/MediaPresentationDescriptionParser.java @@ -22,6 +22,7 @@ import com.google.android.exoplayer.dash.mpd.SegmentBase.SegmentList; import com.google.android.exoplayer.dash.mpd.SegmentBase.SegmentTemplate; import com.google.android.exoplayer.dash.mpd.SegmentBase.SegmentTimelineElement; import com.google.android.exoplayer.dash.mpd.SegmentBase.SingleSegmentBase; +import com.google.android.exoplayer.drm.DrmInitData; import com.google.android.exoplayer.drm.DrmInitData.SchemeData; import com.google.android.exoplayer.extractor.mp4.PsshAtomUtil; import com.google.android.exoplayer.upstream.UriLoadable; @@ -46,8 +47,6 @@ import java.io.IOException; import java.io.InputStream; import java.text.ParseException; import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; import java.util.List; import java.util.UUID; import java.util.regex.Matcher; @@ -234,9 +233,9 @@ public class MediaPresentationDescriptionParser extends DefaultHandler int audioChannels = -1; int audioSamplingRate = parseInt(xpp, "audioSamplingRate", -1); String language = xpp.getAttributeValue(null, "lang"); + ArrayList drmSchemeDatas = new ArrayList<>(); + List representationInfos = new ArrayList<>(); - ContentProtectionsBuilder contentProtectionsBuilder = new ContentProtectionsBuilder(); - List representations = new ArrayList<>(); boolean seenFirstBaseUrl = false; do { xpp.next(); @@ -246,20 +245,19 @@ public class MediaPresentationDescriptionParser extends DefaultHandler seenFirstBaseUrl = true; } } else if (ParserUtil.isStartTag(xpp, "ContentProtection")) { - ContentProtection contentProtection = parseContentProtection(xpp); + SchemeData contentProtection = parseContentProtection(xpp); if (contentProtection != null) { - contentProtectionsBuilder.addAdaptationSetProtection(contentProtection); + drmSchemeDatas.add(contentProtection); } } else if (ParserUtil.isStartTag(xpp, "ContentComponent")) { language = checkLanguageConsistency(language, xpp.getAttributeValue(null, "lang")); contentType = checkContentTypeConsistency(contentType, parseContentType(xpp)); } else if (ParserUtil.isStartTag(xpp, "Representation")) { - Representation representation = parseRepresentation(xpp, baseUrl, mimeType, codecs, width, - height, frameRate, audioChannels, audioSamplingRate, language, segmentBase, - contentProtectionsBuilder); - contentProtectionsBuilder.endRepresentation(); - contentType = checkContentTypeConsistency(contentType, getContentType(representation)); - representations.add(representation); + RepresentationInfo representationInfo = parseRepresentation(xpp, baseUrl, mimeType, codecs, + width, height, frameRate, audioChannels, audioSamplingRate, language, segmentBase); + contentType = checkContentTypeConsistency(contentType, + getContentType(representationInfo.format)); + representationInfos.add(representationInfo); } else if (ParserUtil.isStartTag(xpp, "AudioChannelConfiguration")) { audioChannels = parseAudioChannelConfiguration(xpp); } else if (ParserUtil.isStartTag(xpp, "SegmentBase")) { @@ -273,12 +271,18 @@ public class MediaPresentationDescriptionParser extends DefaultHandler } } while (!ParserUtil.isEndTag(xpp, "AdaptationSet")); - return buildAdaptationSet(id, contentType, representations, contentProtectionsBuilder.build()); + List representations = new ArrayList<>(representationInfos.size()); + for (int i = 0; i < representationInfos.size(); i++) { + representations.add(buildRepresentation(representationInfos.get(i), contentId, + drmSchemeDatas)); + } + + return buildAdaptationSet(id, contentType, representations); } protected AdaptationSet buildAdaptationSet(int id, int contentType, - List representations, List contentProtections) { - return new AdaptationSet(id, contentType, representations, contentProtections); + List representations) { + return new AdaptationSet(id, contentType, representations); } protected int parseContentType(XmlPullParser xpp) { @@ -290,8 +294,8 @@ public class MediaPresentationDescriptionParser extends DefaultHandler : C.TRACK_TYPE_UNKNOWN; } - protected int getContentType(Representation representation) { - String sampleMimeType = representation.format.sampleMimeType; + protected int getContentType(Format format) { + String sampleMimeType = format.sampleMimeType; if (TextUtils.isEmpty(sampleMimeType)) { return C.TRACK_TYPE_UNKNOWN; } else if (MimeTypes.isVideo(sampleMimeType)) { @@ -305,15 +309,15 @@ public class MediaPresentationDescriptionParser extends DefaultHandler } /** - * Parses a {@link ContentProtection} element. + * Parses a ContentProtection element. * * @throws XmlPullParserException If an error occurs parsing the element. * @throws IOException If an error occurs reading the element. - * @return The parsed {@link ContentProtection} element, or null if the element is unsupported. + * @return {@link SchemeData} parsed from the ContentProtection element, or null if the element is + * unsupported. **/ - protected ContentProtection parseContentProtection(XmlPullParser xpp) + protected SchemeData parseContentProtection(XmlPullParser xpp) throws XmlPullParserException, IOException { - String schemeIdUri = xpp.getAttributeValue(null, "schemeIdUri"); SchemeData schemeData = null; boolean seenPsshElement = false; do { @@ -332,11 +336,7 @@ public class MediaPresentationDescriptionParser extends DefaultHandler Log.w(TAG, "Skipped unsupported ContentProtection element"); return null; } - return buildContentProtection(schemeIdUri, schemeData); - } - - protected ContentProtection buildContentProtection(String schemeIdUri, SchemeData schemeData) { - return new ContentProtection(schemeIdUri, schemeData); + return schemeData; } /** @@ -353,11 +353,10 @@ public class MediaPresentationDescriptionParser extends DefaultHandler // Representation parsing. - protected Representation parseRepresentation(XmlPullParser xpp, String baseUrl, + protected RepresentationInfo parseRepresentation(XmlPullParser xpp, String baseUrl, String adaptationSetMimeType, String adaptationSetCodecs, int adaptationSetWidth, int adaptationSetHeight, float adaptationSetFrameRate, int adaptationSetAudioChannels, - int adaptationSetAudioSamplingRate, String adaptationSetLanguage, SegmentBase segmentBase, - ContentProtectionsBuilder contentProtectionsBuilder) + int adaptationSetAudioSamplingRate, String adaptationSetLanguage, SegmentBase segmentBase) throws XmlPullParserException, IOException { String id = xpp.getAttributeValue(null, "id"); int bandwidth = parseInt(xpp, "bandwidth"); @@ -369,6 +368,7 @@ public class MediaPresentationDescriptionParser extends DefaultHandler float frameRate = parseFrameRate(xpp, adaptationSetFrameRate); int audioChannels = adaptationSetAudioChannels; int audioSamplingRate = parseInt(xpp, "audioSamplingRate", adaptationSetAudioSamplingRate); + ArrayList drmSchemeDatas = new ArrayList<>(); boolean seenFirstBaseUrl = false; do { @@ -387,17 +387,18 @@ public class MediaPresentationDescriptionParser extends DefaultHandler } else if (ParserUtil.isStartTag(xpp, "SegmentTemplate")) { segmentBase = parseSegmentTemplate(xpp, baseUrl, (SegmentTemplate) segmentBase); } else if (ParserUtil.isStartTag(xpp, "ContentProtection")) { - ContentProtection contentProtection = parseContentProtection(xpp); + SchemeData contentProtection = parseContentProtection(xpp); if (contentProtection != null) { - contentProtectionsBuilder.addAdaptationSetProtection(contentProtection); + drmSchemeDatas.add(contentProtection); } } } while (!ParserUtil.isEndTag(xpp, "Representation")); Format format = buildFormat(id, mimeType, width, height, frameRate, audioChannels, audioSamplingRate, bandwidth, adaptationSetLanguage, codecs); - return buildRepresentation(contentId, -1, format, - segmentBase != null ? segmentBase : new SingleSegmentBase(baseUrl)); + segmentBase = segmentBase != null ? segmentBase : new SingleSegmentBase(baseUrl); + + return new RepresentationInfo(format, segmentBase, drmSchemeDatas); } protected Format buildFormat(String id, String containerMimeType, int width, int height, @@ -422,9 +423,15 @@ public class MediaPresentationDescriptionParser extends DefaultHandler } } - protected Representation buildRepresentation(String contentId, int revisionId, Format format, - SegmentBase segmentBase) { - return Representation.newInstance(contentId, revisionId, format, segmentBase); + protected Representation buildRepresentation(RepresentationInfo representationInfo, + String contentId, ArrayList extraDrmSchemeDatas) { + Format format = representationInfo.format; + ArrayList drmSchemeDatas = representationInfo.drmSchemeDatas; + drmSchemeDatas.addAll(extraDrmSchemeDatas); + if (!drmSchemeDatas.isEmpty()) { + format = format.copyWithDrmInitData(new DrmInitData(drmSchemeDatas)); + } + return Representation.newInstance(contentId, -1, format, representationInfo.segmentBase); } // SegmentBase, SegmentList and SegmentTemplate parsing. @@ -772,118 +779,17 @@ public class MediaPresentationDescriptionParser extends DefaultHandler return value == null ? defaultValue : value; } - /** - * Builds a list of {@link ContentProtection} elements for an {@link AdaptationSet}. - *

- * If child Representation elements contain ContentProtection elements, then it is required that - * they all define the same ones. If they do, the ContentProtection elements are bubbled up to the - * AdaptationSet. Child Representation elements defining different ContentProtection elements is - * considered an error. - */ - protected static final class ContentProtectionsBuilder implements Comparator { + private static final class RepresentationInfo { - private ArrayList adaptationSetProtections; - private ArrayList representationProtections; - private ArrayList currentRepresentationProtections; + public final Format format; + public final SegmentBase segmentBase; + public final ArrayList drmSchemeDatas; - private boolean representationProtectionsSet; - - /** - * Adds a {@link ContentProtection} found in the AdaptationSet element. - * - * @param contentProtection The {@link ContentProtection} to add. - */ - public void addAdaptationSetProtection(ContentProtection contentProtection) { - if (adaptationSetProtections == null) { - adaptationSetProtections = new ArrayList<>(); - } - maybeAddContentProtection(adaptationSetProtections, contentProtection); - } - - /** - * Adds a {@link ContentProtection} found in a child Representation element. - * - * @param contentProtection The {@link ContentProtection} to add. - */ - public void addRepresentationProtection(ContentProtection contentProtection) { - if (currentRepresentationProtections == null) { - currentRepresentationProtections = new ArrayList<>(); - } - maybeAddContentProtection(currentRepresentationProtections, contentProtection); - } - - /** - * Should be invoked after processing each child Representation element, in order to apply - * consistency checks. - */ - public void endRepresentation() { - if (!representationProtectionsSet) { - if (currentRepresentationProtections != null) { - Collections.sort(currentRepresentationProtections, this); - } - representationProtections = currentRepresentationProtections; - representationProtectionsSet = true; - } else { - // Assert that each Representation element defines the same ContentProtection elements. - if (currentRepresentationProtections == null) { - Assertions.checkState(representationProtections == null); - } else { - Collections.sort(currentRepresentationProtections, this); - Assertions.checkState(currentRepresentationProtections.equals(representationProtections)); - } - } - currentRepresentationProtections = null; - } - - /** - * Returns the final list of consistent {@link ContentProtection} elements. - */ - public ArrayList build() { - if (adaptationSetProtections == null) { - return representationProtections; - } else if (representationProtections == null) { - return adaptationSetProtections; - } else { - // Bubble up ContentProtection elements found in the child Representation elements. - for (int i = 0; i < representationProtections.size(); i++) { - maybeAddContentProtection(adaptationSetProtections, representationProtections.get(i)); - } - return adaptationSetProtections; - } - } - - /** - * Checks a ContentProtection for consistency with the given list, adding it if necessary. - *

    - *
  • If the new ContentProtection matches another in the list, it's consistent and is not - * added to the list. - *
  • If the new ContentProtection has the same schemeUriId as another ContentProtection in the - * list, but its other attributes do not match, then it's inconsistent and an - * {@link IllegalStateException} is thrown. - *
  • Else the new ContentProtection has a unique schemeUriId, it's consistent and is added. - *
- * - * @param contentProtections The list of ContentProtection elements currently known. - * @param contentProtection The ContentProtection to add. - */ - private void maybeAddContentProtection(List contentProtections, - ContentProtection contentProtection) { - if (!contentProtections.contains(contentProtection)) { - for (int i = 0; i < contentProtections.size(); i++) { - // If contains returned false (no complete match), but find a matching schemeUriId, then - // the MPD contains inconsistent ContentProtection data. - Assertions.checkState( - !contentProtections.get(i).schemeUriId.equals(contentProtection.schemeUriId)); - } - contentProtections.add(contentProtection); - } - } - - // Comparator implementation. - - @Override - public int compare(ContentProtection first, ContentProtection second) { - return first.schemeUriId.compareTo(second.schemeUriId); + public RepresentationInfo(Format format, SegmentBase segmentBase, + ArrayList drmSchemeDatas) { + this.format = format; + this.segmentBase = segmentBase; + this.drmSchemeDatas = drmSchemeDatas; } } diff --git a/library/src/main/java/com/google/android/exoplayer/extractor/DefaultTrackOutput.java b/library/src/main/java/com/google/android/exoplayer/extractor/DefaultTrackOutput.java index 471cf63e37..267881e943 100644 --- a/library/src/main/java/com/google/android/exoplayer/extractor/DefaultTrackOutput.java +++ b/library/src/main/java/com/google/android/exoplayer/extractor/DefaultTrackOutput.java @@ -637,6 +637,8 @@ public final class DefaultTrackOutput implements TrackOutput { * buffer is stored in {@code extrasHolder}, along with an encryption id if present and the * absolute position of the first byte that may still be required after the current sample * has been read. + * @param downstreamFormat The current downstream {@link Format}. If the format of the next + * sample is different to the current downstream format then a format will be read. * @param extrasHolder The holder into which extra sample information should be written. * @return The result, which can be {@link TrackStream#NOTHING_READ}, * {@link TrackStream#FORMAT_READ} or {@link TrackStream#BUFFER_READ}. diff --git a/library/src/main/java/com/google/android/exoplayer/extractor/ExtractorOutput.java b/library/src/main/java/com/google/android/exoplayer/extractor/ExtractorOutput.java index e48dae9f84..dd85243788 100644 --- a/library/src/main/java/com/google/android/exoplayer/extractor/ExtractorOutput.java +++ b/library/src/main/java/com/google/android/exoplayer/extractor/ExtractorOutput.java @@ -15,8 +15,6 @@ */ package com.google.android.exoplayer.extractor; -import com.google.android.exoplayer.drm.DrmInitData; - /** * Receives stream level data extracted by an {@link Extractor}. */ @@ -45,11 +43,4 @@ public interface ExtractorOutput { */ void seekMap(SeekMap seekMap); - /** - * Invoked when {@link DrmInitData} has been extracted from the stream. - * - * @param drmInitData The extracted {@link DrmInitData}. - */ - void drmInitData(DrmInitData drmInitData); - } diff --git a/library/src/main/java/com/google/android/exoplayer/extractor/ExtractorSampleSource.java b/library/src/main/java/com/google/android/exoplayer/extractor/ExtractorSampleSource.java index 24a8e64b62..ac2f4b46ae 100644 --- a/library/src/main/java/com/google/android/exoplayer/extractor/ExtractorSampleSource.java +++ b/library/src/main/java/com/google/android/exoplayer/extractor/ExtractorSampleSource.java @@ -24,7 +24,6 @@ import com.google.android.exoplayer.TrackGroup; import com.google.android.exoplayer.TrackGroupArray; import com.google.android.exoplayer.TrackSelection; import com.google.android.exoplayer.TrackStream; -import com.google.android.exoplayer.drm.DrmInitData; import com.google.android.exoplayer.upstream.Allocator; import com.google.android.exoplayer.upstream.DataSource; import com.google.android.exoplayer.upstream.DataSpec; @@ -211,7 +210,6 @@ public final class ExtractorSampleSource implements SampleSource, ExtractorOutpu private volatile boolean tracksBuilt; private volatile SeekMap seekMap; - private volatile DrmInitData drmInitData; private boolean prepared; private boolean seenFirstTrackSelection; @@ -471,12 +469,7 @@ public final class ExtractorSampleSource implements SampleSource, ExtractorOutpu return TrackStream.NOTHING_READ; } - int result = sampleQueues[track].readData(formatHolder, buffer, loadingFinished, - lastSeekPositionUs); - if (result == TrackStream.FORMAT_READ) { - formatHolder.drmInitData = drmInitData; - } - return result; + return sampleQueues[track].readData(formatHolder, buffer, loadingFinished, lastSeekPositionUs); } // Loader.Callback implementation. @@ -530,11 +523,6 @@ public final class ExtractorSampleSource implements SampleSource, ExtractorOutpu this.seekMap = seekMap; } - @Override - public void drmInitData(DrmInitData drmInitData) { - this.drmInitData = drmInitData; - } - // Internal methods. private void seekToInternal(long positionUs) { diff --git a/library/src/main/java/com/google/android/exoplayer/extractor/flv/AudioTagPayloadReader.java b/library/src/main/java/com/google/android/exoplayer/extractor/flv/AudioTagPayloadReader.java index d001f324c9..2fc7f43902 100644 --- a/library/src/main/java/com/google/android/exoplayer/extractor/flv/AudioTagPayloadReader.java +++ b/library/src/main/java/com/google/android/exoplayer/extractor/flv/AudioTagPayloadReader.java @@ -88,7 +88,7 @@ import java.util.Collections; audioSpecifiConfig); Format format = Format.createAudioSampleFormat(null, MimeTypes.AUDIO_AAC, Format.NO_VALUE, Format.NO_VALUE, audioParams.second, audioParams.first, - Collections.singletonList(audioSpecifiConfig), null); + Collections.singletonList(audioSpecifiConfig), null, null); output.format(format); hasOutputFormat = true; } else if (packetType == AAC_PACKET_TYPE_AAC_RAW) { diff --git a/library/src/main/java/com/google/android/exoplayer/extractor/flv/VideoTagPayloadReader.java b/library/src/main/java/com/google/android/exoplayer/extractor/flv/VideoTagPayloadReader.java index bd6aa90f17..d5a338431e 100644 --- a/library/src/main/java/com/google/android/exoplayer/extractor/flv/VideoTagPayloadReader.java +++ b/library/src/main/java/com/google/android/exoplayer/extractor/flv/VideoTagPayloadReader.java @@ -96,7 +96,7 @@ import java.util.List; Format format = Format.createVideoSampleFormat(null, MimeTypes.VIDEO_H264, Format.NO_VALUE, Format.NO_VALUE, avcData.width, avcData.height, Format.NO_VALUE, avcData.initializationData, Format.NO_VALUE, - avcData.pixelWidthAspectRatio); + avcData.pixelWidthAspectRatio, null); output.format(format); hasOutputFormat = true; } else if (packetType == AVC_PACKET_TYPE_AVC_NALU) { diff --git a/library/src/main/java/com/google/android/exoplayer/extractor/mkv/MatroskaExtractor.java b/library/src/main/java/com/google/android/exoplayer/extractor/mkv/MatroskaExtractor.java index 4851acd749..47fe64a4ce 100644 --- a/library/src/main/java/com/google/android/exoplayer/extractor/mkv/MatroskaExtractor.java +++ b/library/src/main/java/com/google/android/exoplayer/extractor/mkv/MatroskaExtractor.java @@ -220,8 +220,7 @@ public final class MatroskaExtractor implements Extractor { // The track corresponding to the current TrackEntry element, or null. private Track currentTrack; - // Whether drm init data has been sent to the output. - private boolean sentDrmInitData; + // Whether a seek map has been sent to the output. private boolean sentSeekMap; // Master seek entry related elements. @@ -483,11 +482,8 @@ public final class MatroskaExtractor implements Extractor { if (currentTrack.encryptionKeyId == null) { throw new ParserException("Encrypted Track found but ContentEncKeyID was not found"); } - if (!sentDrmInitData) { - extractorOutput.drmInitData(new DrmInitData( - new SchemeData(C.UUID_NIL, MimeTypes.VIDEO_WEBM, currentTrack.encryptionKeyId))); - sentDrmInitData = true; - } + currentTrack.drmInitData = new DrmInitData( + new SchemeData(C.UUID_NIL, MimeTypes.VIDEO_WEBM, currentTrack.encryptionKeyId)); } return; case ID_CONTENT_ENCODINGS: @@ -1182,6 +1178,7 @@ public final class MatroskaExtractor implements Extractor { public byte[] sampleStrippedBytes; public byte[] encryptionKeyId; public byte[] codecPrivate; + public DrmInitData drmInitData; // Video elements. public int width = Format.NO_VALUE; @@ -1323,7 +1320,8 @@ public final class MatroskaExtractor implements Extractor { // into the trackId passed when creating the formats. if (MimeTypes.isAudio(mimeType)) { format = Format.createAudioSampleFormat(Integer.toString(trackId), mimeType, - Format.NO_VALUE, maxInputSize, channelCount, sampleRate, initializationData, language); + Format.NO_VALUE, maxInputSize, channelCount, sampleRate, initializationData, language, + drmInitData); } else if (MimeTypes.isVideo(mimeType)) { if (displayUnit == Track.DISPLAY_UNIT_PIXELS) { displayWidth = displayWidth == Format.NO_VALUE ? width : displayWidth; @@ -1335,14 +1333,14 @@ public final class MatroskaExtractor implements Extractor { } format = Format.createVideoSampleFormat(Integer.toString(trackId), mimeType, Format.NO_VALUE, maxInputSize, width, height, Format.NO_VALUE, initializationData, - Format.NO_VALUE, pixelWidthHeightRatio); + Format.NO_VALUE, pixelWidthHeightRatio, drmInitData); } else if (MimeTypes.APPLICATION_SUBRIP.equals(mimeType)) { format = Format.createTextSampleFormat(Integer.toString(trackId), mimeType, Format.NO_VALUE, - language); + language, drmInitData); } else if (MimeTypes.APPLICATION_VOBSUB.equals(mimeType) || MimeTypes.APPLICATION_PGS.equals(mimeType)) { format = Format.createImageSampleFormat(Integer.toString(trackId), mimeType, - Format.NO_VALUE, initializationData, language); + Format.NO_VALUE, initializationData, language, drmInitData); } else { throw new ParserException("Unexpected MIME type."); } diff --git a/library/src/main/java/com/google/android/exoplayer/extractor/mp3/Mp3Extractor.java b/library/src/main/java/com/google/android/exoplayer/extractor/mp3/Mp3Extractor.java index f9da5dd393..ee605bc5cd 100644 --- a/library/src/main/java/com/google/android/exoplayer/extractor/mp3/Mp3Extractor.java +++ b/library/src/main/java/com/google/android/exoplayer/extractor/mp3/Mp3Extractor.java @@ -128,7 +128,7 @@ public final class Mp3Extractor implements Extractor { trackOutput.format(Format.createAudioSampleFormat(null, synchronizedHeader.mimeType, Format.NO_VALUE, MpegAudioHeader.MAX_FRAME_SIZE_BYTES, synchronizedHeader.channels, synchronizedHeader.sampleRate, gaplessInfoHolder.encoderDelay, - gaplessInfoHolder.encoderPadding, null, null)); + gaplessInfoHolder.encoderPadding, null, null, null)); } return readSample(input); } diff --git a/library/src/main/java/com/google/android/exoplayer/extractor/mp4/AtomParsers.java b/library/src/main/java/com/google/android/exoplayer/extractor/mp4/AtomParsers.java index 4e9d874f95..2369f43369 100644 --- a/library/src/main/java/com/google/android/exoplayer/extractor/mp4/AtomParsers.java +++ b/library/src/main/java/com/google/android/exoplayer/extractor/mp4/AtomParsers.java @@ -18,6 +18,7 @@ package com.google.android.exoplayer.extractor.mp4; import com.google.android.exoplayer.C; import com.google.android.exoplayer.Format; import com.google.android.exoplayer.ParserException; +import com.google.android.exoplayer.drm.DrmInitData; import com.google.android.exoplayer.extractor.GaplessInfoHolder; import com.google.android.exoplayer.util.Ac3Util; import com.google.android.exoplayer.util.Assertions; @@ -49,10 +50,12 @@ import java.util.List; * * @param trak Atom to parse. * @param mvhd Movie header atom, used to get the timescale. + * @param drmInitData {@link DrmInitData} to be included in the format. * @param isQuickTime True for QuickTime media. False otherwise. * @return A {@link Track} instance, or {@code null} if the track's type isn't supported. */ - public static Track parseTrak(Atom.ContainerAtom trak, Atom.LeafAtom mvhd, boolean isQuickTime) { + public static Track parseTrak(Atom.ContainerAtom trak, Atom.LeafAtom mvhd, + DrmInitData drmInitData, boolean isQuickTime) { Atom.ContainerAtom mdia = trak.getContainerAtomOfType(Atom.TYPE_mdia); int trackType = parseHdlr(mdia.getLeafAtomOfType(Atom.TYPE_hdlr).data); if (trackType == C.TRACK_TYPE_UNKNOWN) { @@ -73,7 +76,7 @@ import java.util.List; Pair mdhdData = parseMdhd(mdia.getLeafAtomOfType(Atom.TYPE_mdhd).data); StsdData stsdData = parseStsd(stbl.getLeafAtomOfType(Atom.TYPE_stsd).data, tkhdData.id, - tkhdData.rotationDegrees, mdhdData.second, isQuickTime); + tkhdData.rotationDegrees, mdhdData.second, drmInitData, isQuickTime); Pair edtsData = parseEdts(trak.getContainerAtomOfType(Atom.TYPE_edts)); return stsdData.format == null ? null : new Track(tkhdData.id, trackType, mdhdData.first, movieTimescale, durationUs, @@ -560,11 +563,12 @@ import java.util.List; * @param trackId The track's identifier in its container. * @param rotationDegrees The rotation of the track in degrees. * @param language The language of the track. + * @param drmInitData {@link DrmInitData} to be included in the format. * @param isQuickTime True for QuickTime media. False otherwise. * @return An object containing the parsed data. */ private static StsdData parseStsd(ParsableByteArray stsd, int trackId, int rotationDegrees, - String language, boolean isQuickTime) { + String language, DrmInitData drmInitData, boolean isQuickTime) { stsd.setPosition(Atom.FULL_HEADER_SIZE); int numberOfEntries = stsd.readInt(); StsdData out = new StsdData(numberOfEntries); @@ -578,7 +582,7 @@ import java.util.List; || childAtomType == Atom.TYPE_hvc1 || childAtomType == Atom.TYPE_hev1 || childAtomType == Atom.TYPE_s263) { parseVideoSampleEntry(stsd, childStartPosition, childAtomSize, trackId, rotationDegrees, - out, i); + drmInitData, out, i); } else if (childAtomType == Atom.TYPE_mp4a || childAtomType == Atom.TYPE_enca || childAtomType == Atom.TYPE_ac_3 || childAtomType == Atom.TYPE_ec_3 || childAtomType == Atom.TYPE_dtsc || childAtomType == Atom.TYPE_dtse @@ -586,19 +590,19 @@ import java.util.List; || childAtomType == Atom.TYPE_samr || childAtomType == Atom.TYPE_sawb || childAtomType == Atom.TYPE_lpcm || childAtomType == Atom.TYPE_sowt) { parseAudioSampleEntry(stsd, childAtomType, childStartPosition, childAtomSize, trackId, - language, isQuickTime, out, i); + language, isQuickTime, drmInitData, out, i); } else if (childAtomType == Atom.TYPE_TTML) { out.format = Format.createTextSampleFormat(Integer.toString(trackId), - MimeTypes.APPLICATION_TTML, Format.NO_VALUE, language); + MimeTypes.APPLICATION_TTML, Format.NO_VALUE, language, drmInitData); } else if (childAtomType == Atom.TYPE_tx3g) { out.format = Format.createTextSampleFormat(Integer.toString(trackId), - MimeTypes.APPLICATION_TX3G, Format.NO_VALUE, language); + MimeTypes.APPLICATION_TX3G, Format.NO_VALUE, language, drmInitData); } else if (childAtomType == Atom.TYPE_wvtt) { out.format = Format.createTextSampleFormat(Integer.toString(trackId), - MimeTypes.APPLICATION_MP4VTT, Format.NO_VALUE, language); + MimeTypes.APPLICATION_MP4VTT, Format.NO_VALUE, language, drmInitData); } else if (childAtomType == Atom.TYPE_stpp) { out.format = Format.createTextSampleFormat(Integer.toString(trackId), - MimeTypes.APPLICATION_TTML, Format.NO_VALUE, language, + MimeTypes.APPLICATION_TTML, Format.NO_VALUE, language, drmInitData, 0 /* subsample timing is absolute */); } stsd.setPosition(childStartPosition + childAtomSize); @@ -607,7 +611,7 @@ import java.util.List; } private static void parseVideoSampleEntry(ParsableByteArray parent, int position, int size, - int trackId, int rotationDegrees, StsdData out, int entryIndex) { + int trackId, int rotationDegrees, DrmInitData drmInitData, StsdData out, int entryIndex) { parent.setPosition(position + Atom.HEADER_SIZE); parent.skipBytes(24); @@ -671,7 +675,7 @@ import java.util.List; out.format = Format.createVideoSampleFormat(Integer.toString(trackId), mimeType, Format.NO_VALUE, Format.NO_VALUE, width, height, Format.NO_VALUE, initializationData, - rotationDegrees, pixelWidthHeightRatio); + rotationDegrees, pixelWidthHeightRatio, drmInitData); } private static AvcCData parseAvcCFromParent(ParsableByteArray parent, int position) { @@ -830,7 +834,8 @@ import java.util.List; } private static void parseAudioSampleEntry(ParsableByteArray parent, int atomType, int position, - int size, int trackId, String language, boolean isQuickTime, StsdData out, int entryIndex) { + int size, int trackId, String language, boolean isQuickTime, DrmInitData drmInitData, + StsdData out, int entryIndex) { parent.setPosition(position + Atom.HEADER_SIZE); int quickTimeSoundDescriptionVersion = 0; @@ -922,17 +927,20 @@ import java.util.List; // TODO: Choose the right AC-3 track based on the contents of dac3/dec3. // TODO: Add support for encryption (by setting out.trackEncryptionBoxes). parent.setPosition(Atom.HEADER_SIZE + childAtomPosition); - out.format = Ac3Util.parseAc3AnnexFFormat(parent, Integer.toString(trackId), language); + out.format = Ac3Util.parseAc3AnnexFFormat(parent, Integer.toString(trackId), language, + drmInitData); return; } else if (atomType == Atom.TYPE_ec_3 && childAtomType == Atom.TYPE_dec3) { parent.setPosition(Atom.HEADER_SIZE + childAtomPosition); - out.format = Ac3Util.parseEAc3AnnexFFormat(parent, Integer.toString(trackId), language); + out.format = Ac3Util.parseEAc3AnnexFFormat(parent, Integer.toString(trackId), language, + drmInitData); return; } else if ((atomType == Atom.TYPE_dtsc || atomType == Atom.TYPE_dtse || atomType == Atom.TYPE_dtsh || atomType == Atom.TYPE_dtsl) && childAtomType == Atom.TYPE_ddts) { out.format = Format.createAudioSampleFormat(Integer.toString(trackId), mimeType, - Format.NO_VALUE, Format.NO_VALUE, channelCount, sampleRate, null, language); + Format.NO_VALUE, Format.NO_VALUE, channelCount, sampleRate, null, language, + drmInitData); return; } childAtomPosition += childAtomSize; @@ -946,7 +954,7 @@ import java.util.List; out.format = Format.createAudioSampleFormat(Integer.toString(trackId), mimeType, Format.NO_VALUE, Format.NO_VALUE, channelCount, sampleRate, initializationData == null ? null : Collections.singletonList(initializationData), - language); + language, drmInitData); } /** Returns the position of the esds box within a parent, or -1 if no esds box is found */ diff --git a/library/src/main/java/com/google/android/exoplayer/extractor/mp4/FragmentedMp4Extractor.java b/library/src/main/java/com/google/android/exoplayer/extractor/mp4/FragmentedMp4Extractor.java index 9af79a015a..01060f0a4c 100644 --- a/library/src/main/java/com/google/android/exoplayer/extractor/mp4/FragmentedMp4Extractor.java +++ b/library/src/main/java/com/google/android/exoplayer/extractor/mp4/FragmentedMp4Extractor.java @@ -335,9 +335,7 @@ public final class FragmentedMp4Extractor implements Extractor { } } } - if (schemeDatas != null) { - extractorOutput.drmInitData(new DrmInitData(schemeDatas)); - } + DrmInitData drmInitData = schemeDatas == null ? null : new DrmInitData(schemeDatas); // Read declaration of track fragments in the Moov box. ContainerAtom mvex = moov.getContainerAtomOfType(Atom.TYPE_mvex); @@ -357,7 +355,8 @@ public final class FragmentedMp4Extractor implements Extractor { for (int i = 0; i < moovContainerChildrenSize; i++) { Atom.ContainerAtom atom = moov.containerChildren.get(i); if (atom.type == Atom.TYPE_trak) { - Track track = AtomParsers.parseTrak(atom, moov.getLeafAtomOfType(Atom.TYPE_mvhd), false); + Track track = AtomParsers.parseTrak(atom, moov.getLeafAtomOfType(Atom.TYPE_mvhd), + drmInitData, false); if (track != null) { tracks.put(track.id, track); } diff --git a/library/src/main/java/com/google/android/exoplayer/extractor/mp4/Mp4Extractor.java b/library/src/main/java/com/google/android/exoplayer/extractor/mp4/Mp4Extractor.java index 4bbc7e4e49..34b6c61cfb 100644 --- a/library/src/main/java/com/google/android/exoplayer/extractor/mp4/Mp4Extractor.java +++ b/library/src/main/java/com/google/android/exoplayer/extractor/mp4/Mp4Extractor.java @@ -310,7 +310,7 @@ public final class Mp4Extractor implements Extractor, SeekMap { continue; } - Track track = AtomParsers.parseTrak(atom, moov.getLeafAtomOfType(Atom.TYPE_mvhd), + Track track = AtomParsers.parseTrak(atom, moov.getLeafAtomOfType(Atom.TYPE_mvhd), null, isQuickTime); if (track == null) { continue; diff --git a/library/src/main/java/com/google/android/exoplayer/extractor/ogg/FlacReader.java b/library/src/main/java/com/google/android/exoplayer/extractor/ogg/FlacReader.java index f7b19044a9..3d9b4848c4 100644 --- a/library/src/main/java/com/google/android/exoplayer/extractor/ogg/FlacReader.java +++ b/library/src/main/java/com/google/android/exoplayer/extractor/ogg/FlacReader.java @@ -68,7 +68,7 @@ import java.util.List; List initializationData = Collections.singletonList(metadata); trackOutput.format(Format.createAudioSampleFormat(null, MimeTypes.AUDIO_FLAC, streamInfo.bitRate(), Format.NO_VALUE, streamInfo.channels, streamInfo.sampleRate, - initializationData, null)); + initializationData, null, null)); } else if (data[0] == AUDIO_PACKET_TYPE) { if (!firstAudioPacketProcessed) { if (seekTable != null) { diff --git a/library/src/main/java/com/google/android/exoplayer/extractor/ogg/VorbisReader.java b/library/src/main/java/com/google/android/exoplayer/extractor/ogg/VorbisReader.java index d03a5347a9..1953149ee1 100644 --- a/library/src/main/java/com/google/android/exoplayer/extractor/ogg/VorbisReader.java +++ b/library/src/main/java/com/google/android/exoplayer/extractor/ogg/VorbisReader.java @@ -94,7 +94,7 @@ import java.util.ArrayList; trackOutput.format(Format.createAudioSampleFormat(null, MimeTypes.AUDIO_VORBIS, this.vorbisSetup.idHeader.bitrateNominal, OggParser.OGG_MAX_SEGMENT_SIZE * 255, this.vorbisSetup.idHeader.channels, (int) this.vorbisSetup.idHeader.sampleRate, - codecInitialisationData, null)); + codecInitialisationData, null, null)); if (inputLength != C.LENGTH_UNBOUNDED) { oggSeeker.setup(inputLength - audioStartPosition, totalSamples); diff --git a/library/src/main/java/com/google/android/exoplayer/extractor/ts/Ac3Reader.java b/library/src/main/java/com/google/android/exoplayer/extractor/ts/Ac3Reader.java index 3b33be7916..1e76ccb5c7 100644 --- a/library/src/main/java/com/google/android/exoplayer/extractor/ts/Ac3Reader.java +++ b/library/src/main/java/com/google/android/exoplayer/extractor/ts/Ac3Reader.java @@ -162,9 +162,8 @@ import com.google.android.exoplayer.util.ParsableByteArray; */ private void parseHeader() { if (format == null) { - format = isEac3 - ? Ac3Util.parseEac3SyncframeFormat(headerScratchBits, null, null) - : Ac3Util.parseAc3SyncframeFormat(headerScratchBits, null, null); + format = isEac3 ? Ac3Util.parseEac3SyncframeFormat(headerScratchBits, null, null, null) + : Ac3Util.parseAc3SyncframeFormat(headerScratchBits, null, null, null); output.format(format); } sampleSize = isEac3 ? Ac3Util.parseEAc3SyncframeSize(headerScratchBits.data) diff --git a/library/src/main/java/com/google/android/exoplayer/extractor/ts/AdtsReader.java b/library/src/main/java/com/google/android/exoplayer/extractor/ts/AdtsReader.java index 02fbf12bbc..5ea6037237 100644 --- a/library/src/main/java/com/google/android/exoplayer/extractor/ts/AdtsReader.java +++ b/library/src/main/java/com/google/android/exoplayer/extractor/ts/AdtsReader.java @@ -84,7 +84,8 @@ import java.util.Collections; public AdtsReader(TrackOutput output, TrackOutput id3Output) { super(output); this.id3Output = id3Output; - id3Output.format(Format.createSampleFormat(null, MimeTypes.APPLICATION_ID3, Format.NO_VALUE)); + id3Output.format(Format.createSampleFormat(null, MimeTypes.APPLICATION_ID3, Format.NO_VALUE, + null)); adtsScratch = new ParsableBitArray(new byte[HEADER_SIZE + CRC_SIZE]); id3HeaderBuffer = new ParsableByteArray(Arrays.copyOf(ID3_IDENTIFIER, ID3_HEADER_SIZE)); setFindingSampleState(); @@ -275,7 +276,7 @@ import java.util.Collections; Format format = Format.createAudioSampleFormat(null, MimeTypes.AUDIO_AAC, Format.NO_VALUE, Format.NO_VALUE, audioParams.second, audioParams.first, - Collections.singletonList(audioSpecificConfig), null); + Collections.singletonList(audioSpecificConfig), null, null); // In this class a sample is an access unit, but the MediaFormat sample rate specifies the // number of PCM audio samples per second. sampleDurationUs = (C.MICROS_PER_SECOND * 1024) / format.sampleRate; diff --git a/library/src/main/java/com/google/android/exoplayer/extractor/ts/DtsReader.java b/library/src/main/java/com/google/android/exoplayer/extractor/ts/DtsReader.java index cb1d61b722..5ae926b172 100644 --- a/library/src/main/java/com/google/android/exoplayer/extractor/ts/DtsReader.java +++ b/library/src/main/java/com/google/android/exoplayer/extractor/ts/DtsReader.java @@ -155,7 +155,7 @@ import com.google.android.exoplayer.util.ParsableByteArray; private void parseHeader() { byte[] frameData = headerScratchBytes.data; if (format == null) { - format = DtsUtil.parseDtsFormat(frameData, null, null); + format = DtsUtil.parseDtsFormat(frameData, null, null, null); output.format(format); } sampleSize = DtsUtil.getDtsFrameSize(frameData); diff --git a/library/src/main/java/com/google/android/exoplayer/extractor/ts/H262Reader.java b/library/src/main/java/com/google/android/exoplayer/extractor/ts/H262Reader.java index b9de7f9df2..d5b5148ef0 100644 --- a/library/src/main/java/com/google/android/exoplayer/extractor/ts/H262Reader.java +++ b/library/src/main/java/com/google/android/exoplayer/extractor/ts/H262Reader.java @@ -191,7 +191,7 @@ import java.util.Collections; Format format = Format.createVideoSampleFormat(null, MimeTypes.VIDEO_MPEG2, Format.NO_VALUE, Format.NO_VALUE, width, height, Format.NO_VALUE, Collections.singletonList(csdData), - Format.NO_VALUE, pixelWidthHeightRatio); + Format.NO_VALUE, pixelWidthHeightRatio, null); long frameDurationUs = 0; int frameRateCodeMinusOne = (csdData[7] & 0x0F) - 1; diff --git a/library/src/main/java/com/google/android/exoplayer/extractor/ts/H264Reader.java b/library/src/main/java/com/google/android/exoplayer/extractor/ts/H264Reader.java index 7a15421ab6..f93b82a6e9 100644 --- a/library/src/main/java/com/google/android/exoplayer/extractor/ts/H264Reader.java +++ b/library/src/main/java/com/google/android/exoplayer/extractor/ts/H264Reader.java @@ -171,7 +171,7 @@ import java.util.List; NalUnitUtil.PpsData ppsData = NalUnitUtil.parsePpsNalUnit(pps.nalData, 3, pps.nalLength); output.format(Format.createVideoSampleFormat(null, MimeTypes.VIDEO_H264, Format.NO_VALUE, Format.NO_VALUE, spsData.width, spsData.height, Format.NO_VALUE, initializationData, - Format.NO_VALUE, spsData.pixelWidthAspectRatio)); + Format.NO_VALUE, spsData.pixelWidthAspectRatio, null)); hasOutputFormat = true; sampleReader.putSps(spsData); sampleReader.putPps(ppsData); diff --git a/library/src/main/java/com/google/android/exoplayer/extractor/ts/H265Reader.java b/library/src/main/java/com/google/android/exoplayer/extractor/ts/H265Reader.java index df4441d876..8d7c557cf7 100644 --- a/library/src/main/java/com/google/android/exoplayer/extractor/ts/H265Reader.java +++ b/library/src/main/java/com/google/android/exoplayer/extractor/ts/H265Reader.java @@ -309,7 +309,7 @@ import java.util.Collections; return Format.createVideoSampleFormat(null, MimeTypes.VIDEO_H265, Format.NO_VALUE, Format.NO_VALUE, picWidthInLumaSamples, picHeightInLumaSamples, Format.NO_VALUE, - Collections.singletonList(csd), Format.NO_VALUE, pixelWidthHeightRatio); + Collections.singletonList(csd), Format.NO_VALUE, pixelWidthHeightRatio, null); } /** diff --git a/library/src/main/java/com/google/android/exoplayer/extractor/ts/Id3Reader.java b/library/src/main/java/com/google/android/exoplayer/extractor/ts/Id3Reader.java index ccd0e79302..a9c1406602 100644 --- a/library/src/main/java/com/google/android/exoplayer/extractor/ts/Id3Reader.java +++ b/library/src/main/java/com/google/android/exoplayer/extractor/ts/Id3Reader.java @@ -40,7 +40,8 @@ import com.google.android.exoplayer.util.ParsableByteArray; public Id3Reader(TrackOutput output) { super(output); - output.format(Format.createSampleFormat(null, MimeTypes.APPLICATION_ID3, Format.NO_VALUE)); + output.format(Format.createSampleFormat(null, MimeTypes.APPLICATION_ID3, Format.NO_VALUE, + null)); id3Header = new ParsableByteArray(ID3_HEADER_SIZE); } diff --git a/library/src/main/java/com/google/android/exoplayer/extractor/ts/MpegAudioReader.java b/library/src/main/java/com/google/android/exoplayer/extractor/ts/MpegAudioReader.java index b75e095796..5571736bed 100644 --- a/library/src/main/java/com/google/android/exoplayer/extractor/ts/MpegAudioReader.java +++ b/library/src/main/java/com/google/android/exoplayer/extractor/ts/MpegAudioReader.java @@ -163,7 +163,8 @@ import com.google.android.exoplayer.util.ParsableByteArray; if (!hasOutputFormat) { frameDurationUs = (C.MICROS_PER_SECOND * header.samplesPerFrame) / header.sampleRate; Format format = Format.createAudioSampleFormat(null, header.mimeType, Format.NO_VALUE, - MpegAudioHeader.MAX_FRAME_SIZE_BYTES, header.channels, header.sampleRate, null, null); + MpegAudioHeader.MAX_FRAME_SIZE_BYTES, header.channels, header.sampleRate, null, null, + null); output.format(format); hasOutputFormat = true; } diff --git a/library/src/main/java/com/google/android/exoplayer/extractor/ts/SeiReader.java b/library/src/main/java/com/google/android/exoplayer/extractor/ts/SeiReader.java index 49ccbef2f1..a73c5beb8d 100644 --- a/library/src/main/java/com/google/android/exoplayer/extractor/ts/SeiReader.java +++ b/library/src/main/java/com/google/android/exoplayer/extractor/ts/SeiReader.java @@ -34,7 +34,7 @@ import com.google.android.exoplayer.util.ParsableByteArray; public SeiReader(TrackOutput output) { this.output = output; output.format(Format.createTextSampleFormat(null, MimeTypes.APPLICATION_EIA608, Format.NO_VALUE, - null)); + null, null)); } public void consume(long pesTimeUs, ParsableByteArray seiBuffer) { diff --git a/library/src/main/java/com/google/android/exoplayer/extractor/wav/WavExtractor.java b/library/src/main/java/com/google/android/exoplayer/extractor/wav/WavExtractor.java index 0de76585a7..cb7fe9aab0 100644 --- a/library/src/main/java/com/google/android/exoplayer/extractor/wav/WavExtractor.java +++ b/library/src/main/java/com/google/android/exoplayer/extractor/wav/WavExtractor.java @@ -74,7 +74,7 @@ public final class WavExtractor implements Extractor, SeekMap { } Format format = Format.createAudioSampleFormat(null, MimeTypes.AUDIO_RAW, wavHeader.getBitrate(), MAX_INPUT_SIZE, wavHeader.getNumChannels(), - wavHeader.getSampleRateHz(), null, null); + wavHeader.getSampleRateHz(), null, null, null); trackOutput.format(format); bytesPerFrame = wavHeader.getBytesPerFrame(); } diff --git a/library/src/main/java/com/google/android/exoplayer/hls/HlsOutput.java b/library/src/main/java/com/google/android/exoplayer/hls/HlsOutput.java index c11da36171..222177326c 100644 --- a/library/src/main/java/com/google/android/exoplayer/hls/HlsOutput.java +++ b/library/src/main/java/com/google/android/exoplayer/hls/HlsOutput.java @@ -15,7 +15,6 @@ */ package com.google.android.exoplayer.hls; -import com.google.android.exoplayer.drm.DrmInitData; import com.google.android.exoplayer.extractor.DefaultTrackOutput; import com.google.android.exoplayer.extractor.ExtractorOutput; import com.google.android.exoplayer.extractor.SeekMap; @@ -117,9 +116,4 @@ import android.util.SparseArray; // Do nothing. } - @Override - public void drmInitData(DrmInitData drmInitData) { - // Do nothing. - } - } diff --git a/library/src/main/java/com/google/android/exoplayer/hls/WebvttExtractor.java b/library/src/main/java/com/google/android/exoplayer/hls/WebvttExtractor.java index 080f9f3134..3d1aed8f94 100644 --- a/library/src/main/java/com/google/android/exoplayer/hls/WebvttExtractor.java +++ b/library/src/main/java/com/google/android/exoplayer/hls/WebvttExtractor.java @@ -166,7 +166,7 @@ import java.util.regex.Pattern; private TrackOutput buildTrackOutput(long subsampleOffsetUs) { TrackOutput trackOutput = output.track(0); trackOutput.format(Format.createTextSampleFormat(null, MimeTypes.TEXT_VTT, Format.NO_VALUE, - language, subsampleOffsetUs)); + language, null, subsampleOffsetUs)); output.endTracks(); return trackOutput; } diff --git a/library/src/main/java/com/google/android/exoplayer/smoothstreaming/SmoothStreamingChunkSource.java b/library/src/main/java/com/google/android/exoplayer/smoothstreaming/SmoothStreamingChunkSource.java index bc4ada7ca4..086cfdcdea 100644 --- a/library/src/main/java/com/google/android/exoplayer/smoothstreaming/SmoothStreamingChunkSource.java +++ b/library/src/main/java/com/google/android/exoplayer/smoothstreaming/SmoothStreamingChunkSource.java @@ -28,7 +28,6 @@ import com.google.android.exoplayer.chunk.ContainerMediaChunk; import com.google.android.exoplayer.chunk.FormatEvaluator; import com.google.android.exoplayer.chunk.FormatEvaluator.Evaluation; import com.google.android.exoplayer.chunk.MediaChunk; -import com.google.android.exoplayer.drm.DrmInitData; import com.google.android.exoplayer.extractor.mp4.FragmentedMp4Extractor; import com.google.android.exoplayer.extractor.mp4.Track; import com.google.android.exoplayer.extractor.mp4.TrackEncryptionBox; @@ -58,7 +57,6 @@ public class SmoothStreamingChunkSource implements ChunkSource { private final FormatEvaluator adaptiveFormatEvaluator; private SmoothStreamingManifest manifest; - private DrmInitData drmInitData; private int currentManifestChunkOffset; private boolean needManifestRefresh; @@ -72,18 +70,15 @@ public class SmoothStreamingChunkSource implements ChunkSource { * @param dataSource A {@link DataSource} suitable for loading the media data. * @param adaptiveFormatEvaluator For adaptive tracks, selects from the available formats. * @param trackEncryptionBoxes Track encryption boxes for the stream. - * @param drmInitData Drm initialization data for the stream. */ public SmoothStreamingChunkSource(SmoothStreamingManifest manifest, int elementIndex, TrackGroup trackGroup, int[] tracks, DataSource dataSource, - FormatEvaluator adaptiveFormatEvaluator, TrackEncryptionBox[] trackEncryptionBoxes, - DrmInitData drmInitData) { + FormatEvaluator adaptiveFormatEvaluator, TrackEncryptionBox[] trackEncryptionBoxes) { this.manifest = manifest; this.elementIndex = elementIndex; this.trackGroup = trackGroup; this.dataSource = dataSource; this.adaptiveFormatEvaluator = adaptiveFormatEvaluator; - this.drmInitData = drmInitData; this.evaluation = new Evaluation(); StreamElement streamElement = manifest.streamElements[elementIndex]; @@ -97,7 +92,7 @@ public class SmoothStreamingChunkSource implements ChunkSource { FragmentedMp4Extractor extractor = new FragmentedMp4Extractor( FragmentedMp4Extractor.FLAG_WORKAROUND_EVERY_VIDEO_FRAME_IS_SYNC_FRAME | FragmentedMp4Extractor.FLAG_WORKAROUND_IGNORE_TFDT_BOX, track); - extractorWrappers[j] = new ChunkExtractorWrapper(extractor); + extractorWrappers[j] = new ChunkExtractorWrapper(extractor, formats[j].drmInitData); } enabledFormats = new Format[tracks.length]; @@ -224,8 +219,7 @@ public class SmoothStreamingChunkSource implements ChunkSource { Uri uri = streamElement.buildRequestUri(manifestTrackIndex, chunkIndex); out.chunk = newMediaChunk(selectedFormat, dataSource, uri, null, currentAbsoluteChunkIndex, - chunkStartTimeUs, chunkEndTimeUs, evaluation.trigger, extractorWrapper, drmInitData, - selectedFormat); + chunkStartTimeUs, chunkEndTimeUs, evaluation.trigger, extractorWrapper); } @Override @@ -274,13 +268,13 @@ public class SmoothStreamingChunkSource implements ChunkSource { private static MediaChunk newMediaChunk(Format format, DataSource dataSource, Uri uri, String cacheKey, int chunkIndex, long chunkStartTimeUs, long chunkEndTimeUs, int trigger, - ChunkExtractorWrapper extractorWrapper, DrmInitData drmInitData, Format sampleFormat) { + ChunkExtractorWrapper extractorWrapper) { DataSpec dataSpec = new DataSpec(uri, 0, -1, cacheKey); // In SmoothStreaming each chunk contains sample timestamps relative to the start of the chunk. // To convert them the absolute timestamps, we need to set sampleOffsetUs to chunkStartTimeUs. long sampleOffsetUs = chunkStartTimeUs; return new ContainerMediaChunk(dataSource, dataSpec, trigger, format, chunkStartTimeUs, - chunkEndTimeUs, chunkIndex, sampleOffsetUs, extractorWrapper, sampleFormat, drmInitData); + chunkEndTimeUs, chunkIndex, sampleOffsetUs, extractorWrapper, format); } } diff --git a/library/src/main/java/com/google/android/exoplayer/smoothstreaming/SmoothStreamingManifestParser.java b/library/src/main/java/com/google/android/exoplayer/smoothstreaming/SmoothStreamingManifestParser.java index dad1f06bbc..6340ca98ce 100644 --- a/library/src/main/java/com/google/android/exoplayer/smoothstreaming/SmoothStreamingManifestParser.java +++ b/library/src/main/java/com/google/android/exoplayer/smoothstreaming/SmoothStreamingManifestParser.java @@ -18,6 +18,8 @@ package com.google.android.exoplayer.smoothstreaming; import com.google.android.exoplayer.C; import com.google.android.exoplayer.Format; import com.google.android.exoplayer.ParserException; +import com.google.android.exoplayer.drm.DrmInitData; +import com.google.android.exoplayer.drm.DrmInitData.SchemeData; import com.google.android.exoplayer.extractor.mp4.PsshAtomUtil; import com.google.android.exoplayer.smoothstreaming.SmoothStreamingManifest.ProtectionElement; import com.google.android.exoplayer.smoothstreaming.SmoothStreamingManifest.StreamElement; @@ -375,6 +377,15 @@ public class SmoothStreamingManifestParser implements UriLoadable.Parser> 6; int sampleRate = SAMPLE_RATE_BY_FSCOD[fscod]; int nextByte = data.readUnsignedByte(); @@ -78,7 +80,7 @@ public final class Ac3Util { channelCount++; } return Format.createAudioSampleFormat(trackId, MimeTypes.AUDIO_AC3, Format.NO_VALUE, - Format.NO_VALUE, channelCount, sampleRate, null, language); + Format.NO_VALUE, channelCount, sampleRate, null, language, drmInitData); } /** @@ -88,10 +90,11 @@ public final class Ac3Util { * @param data The EC3SpecificBox to parse. * @param trackId The track identifier to set on the format, or null. * @param language The language to set on the format. + * @param drmInitData {@link DrmInitData} to be included in the format. * @return The E-AC-3 format parsed from data in the header. */ public static Format parseEAc3AnnexFFormat(ParsableByteArray data, String trackId, - String language) { + String language, DrmInitData drmInitData) { data.skipBytes(2); // data_rate, num_ind_sub // Read only the first substream. @@ -104,7 +107,7 @@ public final class Ac3Util { channelCount++; } return Format.createAudioSampleFormat(trackId, MimeTypes.AUDIO_E_AC3, Format.NO_VALUE, - Format.NO_VALUE, channelCount, sampleRate, null, language); + Format.NO_VALUE, channelCount, sampleRate, null, language, drmInitData); } /** @@ -114,10 +117,11 @@ public final class Ac3Util { * @param data The data to parse, positioned at the start of the syncframe. * @param trackId The track identifier to set on the format, or null. * @param language The language to set on the format. + * @param drmInitData {@link DrmInitData} to be included in the format. * @return The AC-3 format parsed from data in the header. */ public static Format parseAc3SyncframeFormat(ParsableBitArray data, String trackId, - String language) { + String language, DrmInitData drmInitData) { data.skipBits(16 + 16); // syncword, crc1 int fscod = data.readBits(2); data.skipBits(6 + 5 + 3); // frmsizecod, bsid, bsmod @@ -134,7 +138,7 @@ public final class Ac3Util { boolean lfeon = data.readBit(); return Format.createAudioSampleFormat(trackId, MimeTypes.AUDIO_AC3, Format.NO_VALUE, Format.NO_VALUE, CHANNEL_COUNT_BY_ACMOD[acmod] + (lfeon ? 1 : 0), - SAMPLE_RATE_BY_FSCOD[fscod], null, language); + SAMPLE_RATE_BY_FSCOD[fscod], null, language, drmInitData); } /** @@ -144,10 +148,11 @@ public final class Ac3Util { * @param data The data to parse, positioned at the start of the syncframe. * @param trackId The track identifier to set on the format, or null. * @param language The language to set on the format. + * @param drmInitData {@link DrmInitData} to be included in the format. * @return The E-AC-3 format parsed from data in the header. */ public static Format parseEac3SyncframeFormat(ParsableBitArray data, String trackId, - String language) { + String language, DrmInitData drmInitData) { data.skipBits(16 + 2 + 3 + 11); // syncword, strmtype, substreamid, frmsiz int sampleRate; int fscod = data.readBits(2); @@ -161,7 +166,7 @@ public final class Ac3Util { boolean lfeon = data.readBit(); return Format.createAudioSampleFormat(trackId, MimeTypes.AUDIO_E_AC3, Format.NO_VALUE, Format.NO_VALUE, CHANNEL_COUNT_BY_ACMOD[acmod] + (lfeon ? 1 : 0), sampleRate, null, - language); + language, drmInitData); } /** diff --git a/library/src/main/java/com/google/android/exoplayer/util/DtsUtil.java b/library/src/main/java/com/google/android/exoplayer/util/DtsUtil.java index 2aba6d0407..e1aede50ec 100644 --- a/library/src/main/java/com/google/android/exoplayer/util/DtsUtil.java +++ b/library/src/main/java/com/google/android/exoplayer/util/DtsUtil.java @@ -16,6 +16,7 @@ package com.google.android.exoplayer.util; import com.google.android.exoplayer.Format; +import com.google.android.exoplayer.drm.DrmInitData; import java.nio.ByteBuffer; @@ -54,9 +55,11 @@ public final class DtsUtil { * @param frame The DTS frame to parse. * @param trackId The track identifier to set on the format, or null. * @param language The language to set on the format. + * @param drmInitData {@link DrmInitData} to be included in the format. * @return The DTS format parsed from data in the header. */ - public static Format parseDtsFormat(byte[] frame, String trackId, String language) { + public static Format parseDtsFormat(byte[] frame, String trackId, String language, + DrmInitData drmInitData) { ParsableBitArray frameBits = SCRATCH_BITS; frameBits.reset(frame); frameBits.skipBits(4 * 8 + 1 + 5 + 1 + 7 + 14); // SYNC, FTYPE, SHORT, CPF, NBLKS, FSIZE @@ -70,7 +73,7 @@ public final class DtsUtil { frameBits.skipBits(10); // MIX, DYNF, TIMEF, AUXF, HDCD, EXT_AUDIO_ID, EXT_AUDIO, ASPF channelCount += frameBits.readBits(2) > 0 ? 1 : 0; // LFF return Format.createAudioSampleFormat(trackId, MimeTypes.AUDIO_DTS, bitrate, Format.NO_VALUE, - channelCount, sampleRate, null, language); + channelCount, sampleRate, null, language, drmInitData); } /**