Commit 9f2707cf by olly Committed by Oliver Woodman

Push DrmInitData into Format.

- This means DrmInitData is propagated through sample queues (i.e.
  is effectively attached to every sample, so we can see when it
  changes when reading from the queue).
- It also allows different DrmInitData per track, which is possible
  in muxed MKV/WebM, and per Representation for DASH, although we
  wont be able to seamlessly adapt in the latter case.
-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=121821928
parent e284d616
Showing with 194 additions and 357 deletions
......@@ -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());
......
......@@ -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")
......
......@@ -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);
......
......@@ -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));
......
......@@ -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;
}
......@@ -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;
}
......
......@@ -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;
......
......@@ -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;
......@@ -65,13 +64,6 @@ public abstract class BaseMediaChunk extends MediaChunk {
}
/**
* 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)}.
*/
protected final DefaultTrackOutput getTrackOutput() {
......
......@@ -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);
}
......
......@@ -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.
......
......@@ -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
......
......@@ -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;
......@@ -78,15 +76,6 @@ public final class InitializationChunk extends Chunk implements SingleTrackMetad
}
/**
* Returns a {@link DrmInitData} parsed from the chunk, or null.
* <p>
* Should be called after loading has completed.
*/
public DrmInitData getDrmInitData() {
return drmInitData;
}
/**
* Returns a {@link SeekMap} parsed from the chunk, or null.
* <p>
* Should be called after loading has completed.
......@@ -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
......
......@@ -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
......
......@@ -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<Representation> 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<SchemeData> 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<SchemeData>();
}
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();
......
......@@ -28,26 +28,11 @@ public class AdaptationSet {
public final int type;
public final List<Representation> representations;
public final List<ContentProtection> contentProtections;
public AdaptationSet(int id, int type, List<Representation> representations,
List<ContentProtection> contentProtections) {
public AdaptationSet(int id, int type, List<Representation> 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<Representation> representations) {
this(id, type, representations, null);
}
public boolean hasContentProtection() {
return !contentProtections.isEmpty();
}
}
/*
* 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);
}
}
......@@ -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}.
......
......@@ -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);
}
......@@ -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) {
......
......@@ -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) {
......
......@@ -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) {
......
......@@ -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.");
}
......
......@@ -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);
}
......
......@@ -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<Long, String> 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<long[], long[]> 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 */
......
......@@ -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);
}
......
......@@ -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;
......
......@@ -68,7 +68,7 @@ import java.util.List;
List<byte[]> 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) {
......
......@@ -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);
......
......@@ -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)
......
......@@ -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;
......
......@@ -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);
......
......@@ -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;
......
......@@ -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);
......
......@@ -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);
}
/**
......
......@@ -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);
}
......
......@@ -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;
}
......
......@@ -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) {
......
......@@ -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();
}
......
......@@ -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.
}
}
......@@ -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;
}
......
......@@ -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);
}
}
......@@ -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<SmoothS
public Object build() {
StreamElement[] streamElementArray = new StreamElement[streamElements.size()];
streamElements.toArray(streamElementArray);
if (protectionElement != null) {
DrmInitData drmInitData = new DrmInitData(new SchemeData(protectionElement.uuid,
MimeTypes.VIDEO_MP4, protectionElement.data));
for (StreamElement streamElement : streamElementArray) {
for (int i = 0; i < streamElement.formats.length; i++) {
streamElement.formats[i] = streamElement.formats[i].copyWithDrmInitData(drmInitData);
}
}
}
return new SmoothStreamingManifest(majorVersion, minorVersion, timescale, duration,
dvrWindowLength, lookAheadCount, isLive, protectionElement, streamElementArray);
}
......
......@@ -28,8 +28,6 @@ import com.google.android.exoplayer.chunk.ChunkTrackStream;
import com.google.android.exoplayer.chunk.ChunkTrackStreamEventListener;
import com.google.android.exoplayer.chunk.FormatEvaluator;
import com.google.android.exoplayer.chunk.FormatEvaluator.AdaptiveEvaluator;
import com.google.android.exoplayer.drm.DrmInitData;
import com.google.android.exoplayer.drm.DrmInitData.SchemeData;
import com.google.android.exoplayer.extractor.mp4.TrackEncryptionBox;
import com.google.android.exoplayer.smoothstreaming.SmoothStreamingManifest.ProtectionElement;
import com.google.android.exoplayer.smoothstreaming.SmoothStreamingManifest.StreamElement;
......@@ -38,7 +36,6 @@ import com.google.android.exoplayer.upstream.DataSource;
import com.google.android.exoplayer.upstream.DataSourceFactory;
import com.google.android.exoplayer.upstream.DefaultAllocator;
import com.google.android.exoplayer.util.ManifestFetcher;
import com.google.android.exoplayer.util.MimeTypes;
import com.google.android.exoplayer.util.Util;
import android.net.Uri;
......@@ -69,7 +66,6 @@ public final class SmoothStreamingSampleSource implements SampleSource {
private long durationUs;
private SmoothStreamingManifest currentManifest;
private TrackEncryptionBox[] trackEncryptionBoxes;
private DrmInitData drmInitData;
private TrackGroupArray trackGroups;
private int[] trackGroupElementIndices;
private boolean pendingReset;
......@@ -117,10 +113,8 @@ public final class SmoothStreamingSampleSource implements SampleSource {
ProtectionElement protectionElement = currentManifest.protectionElement;
if (protectionElement != null) {
byte[] keyId = getProtectionElementKeyId(protectionElement.data);
trackEncryptionBoxes = new TrackEncryptionBox[1];
trackEncryptionBoxes[0] = new TrackEncryptionBox(true, INITIALIZATION_VECTOR_SIZE, keyId);
drmInitData = new DrmInitData(
new SchemeData(protectionElement.uuid, MimeTypes.VIDEO_MP4, protectionElement.data));
trackEncryptionBoxes = new TrackEncryptionBox[] {
new TrackEncryptionBox(true, INITIALIZATION_VECTOR_SIZE, keyId)};
}
return true;
}
......@@ -279,7 +273,7 @@ public final class SmoothStreamingSampleSource implements SampleSource {
DataSource dataSource = dataSourceFactory.createDataSource(bandwidthMeter);
SmoothStreamingChunkSource chunkSource = new SmoothStreamingChunkSource(currentManifest,
streamElementIndex, trackGroups.get(selection.group), selectedTracks, dataSource,
adaptiveEvaluator, trackEncryptionBoxes, drmInitData);
adaptiveEvaluator, trackEncryptionBoxes);
ChunkTrackStream trackStream = new ChunkTrackStream(chunkSource, loadControl, bufferSize,
positionUs, eventHandler, eventListener, streamElementType);
return Pair.create(chunkSource, trackStream);
......
......@@ -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;
......@@ -66,10 +67,11 @@ public final class Ac3Util {
* @param data The AC3SpecificBox 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 AC-3 format parsed from data in the header.
*/
public static Format parseAc3AnnexFFormat(ParsableByteArray data, String trackId,
String language) {
String language, DrmInitData drmInitData) {
int fscod = (data.readUnsignedByte() & 0xC0) >> 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);
}
/**
......
......@@ -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);
}
/**
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment