Commit c4b2a012 by Oliver Woodman

Allow out-of-band pssh data for DASH playbacks.

This fixes the referenced issue, except that the MPD parser
needs to actually parse out UUID and binary data for schemes
that we wish to support. Alternatively, it's easy to applications
to do this themselves by extending the parser and overriding
the parseContentProtection and buildContentProtection methods.

Github Issue: #119
parent 2f0a1779
...@@ -31,6 +31,7 @@ import com.google.android.exoplayer.chunk.MediaChunk; ...@@ -31,6 +31,7 @@ import com.google.android.exoplayer.chunk.MediaChunk;
import com.google.android.exoplayer.chunk.Mp4MediaChunk; import com.google.android.exoplayer.chunk.Mp4MediaChunk;
import com.google.android.exoplayer.chunk.SingleSampleMediaChunk; import com.google.android.exoplayer.chunk.SingleSampleMediaChunk;
import com.google.android.exoplayer.dash.mpd.AdaptationSet; 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.MediaPresentationDescription;
import com.google.android.exoplayer.dash.mpd.Period; import com.google.android.exoplayer.dash.mpd.Period;
import com.google.android.exoplayer.dash.mpd.RangedUri; import com.google.android.exoplayer.dash.mpd.RangedUri;
...@@ -53,6 +54,8 @@ import java.util.Arrays; ...@@ -53,6 +54,8 @@ import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.UUID;
/** /**
* An {@link ChunkSource} for DASH streams. * An {@link ChunkSource} for DASH streams.
...@@ -92,6 +95,7 @@ public class DashChunkSource implements ChunkSource { ...@@ -92,6 +95,7 @@ public class DashChunkSource implements ChunkSource {
private final ManifestFetcher<MediaPresentationDescription> manifestFetcher; private final ManifestFetcher<MediaPresentationDescription> manifestFetcher;
private final int adaptationSetIndex; private final int adaptationSetIndex;
private final int[] representationIndices; private final int[] representationIndices;
private final Map<UUID, byte[]> psshInfo;
private MediaPresentationDescription currentManifest; private MediaPresentationDescription currentManifest;
private boolean finishedCurrentManifest; private boolean finishedCurrentManifest;
...@@ -180,6 +184,7 @@ public class DashChunkSource implements ChunkSource { ...@@ -180,6 +184,7 @@ public class DashChunkSource implements ChunkSource {
this.evaluation = new Evaluation(); this.evaluation = new Evaluation();
this.headerBuilder = new StringBuilder(); this.headerBuilder = new StringBuilder();
psshInfo = getPsshInfo(currentManifest, adaptationSetIndex);
Representation[] representations = getFilteredRepresentations(currentManifest, Representation[] representations = getFilteredRepresentations(currentManifest,
adaptationSetIndex, representationIndices); adaptationSetIndex, representationIndices);
long periodDurationUs = (representations[0].periodDurationMs == TrackRenderer.UNKNOWN_TIME_US) long periodDurationUs = (representations[0].periodDurationMs == TrackRenderer.UNKNOWN_TIME_US)
...@@ -438,7 +443,7 @@ public class DashChunkSource implements ChunkSource { ...@@ -438,7 +443,7 @@ public class DashChunkSource implements ChunkSource {
startTimeUs, endTimeUs, nextAbsoluteSegmentNum, null, representationHolder.vttHeader); startTimeUs, endTimeUs, nextAbsoluteSegmentNum, null, representationHolder.vttHeader);
} else { } else {
return new Mp4MediaChunk(dataSource, dataSpec, representation.format, trigger, startTimeUs, return new Mp4MediaChunk(dataSource, dataSpec, representation.format, trigger, startTimeUs,
endTimeUs, nextAbsoluteSegmentNum, representationHolder.extractor, null, false, endTimeUs, nextAbsoluteSegmentNum, representationHolder.extractor, psshInfo, false,
presentationTimeOffsetUs); presentationTimeOffsetUs);
} }
} }
...@@ -463,8 +468,8 @@ public class DashChunkSource implements ChunkSource { ...@@ -463,8 +468,8 @@ public class DashChunkSource implements ChunkSource {
private static Representation[] getFilteredRepresentations(MediaPresentationDescription manifest, private static Representation[] getFilteredRepresentations(MediaPresentationDescription manifest,
int adaptationSetIndex, int[] representationIndices) { int adaptationSetIndex, int[] representationIndices) {
List<Representation> representations = AdaptationSet adaptationSet = manifest.periods.get(0).adaptationSets.get(adaptationSetIndex);
manifest.periods.get(0).adaptationSets.get(adaptationSetIndex).representations; List<Representation> representations = adaptationSet.representations;
if (representationIndices == null) { if (representationIndices == null) {
Representation[] filteredRepresentations = new Representation[representations.size()]; Representation[] filteredRepresentations = new Representation[representations.size()];
representations.toArray(filteredRepresentations); representations.toArray(filteredRepresentations);
...@@ -478,6 +483,22 @@ public class DashChunkSource implements ChunkSource { ...@@ -478,6 +483,22 @@ public class DashChunkSource implements ChunkSource {
} }
} }
private static Map<UUID, byte[]> getPsshInfo(MediaPresentationDescription manifest,
int adaptationSetIndex) {
AdaptationSet adaptationSet = manifest.periods.get(0).adaptationSets.get(adaptationSetIndex);
if (adaptationSet.contentProtections.isEmpty()) {
return null;
} else {
Map<UUID, byte[]> psshInfo = new HashMap<UUID, byte[]>();
for (ContentProtection contentProtection : adaptationSet.contentProtections) {
if (contentProtection.uuid != null && contentProtection.data != null) {
psshInfo.put(contentProtection.uuid, contentProtection.data);
}
}
return psshInfo.isEmpty() ? null : psshInfo;
}
}
private static MediaPresentationDescription buildManifest(List<Representation> representations) { private static MediaPresentationDescription buildManifest(List<Representation> representations) {
Representation firstRepresentation = representations.get(0); Representation firstRepresentation = representations.get(0);
AdaptationSet adaptationSet = new AdaptationSet(0, AdaptationSet.TYPE_UNKNOWN, representations); AdaptationSet adaptationSet = new AdaptationSet(0, AdaptationSet.TYPE_UNKNOWN, representations);
......
...@@ -15,6 +15,8 @@ ...@@ -15,6 +15,8 @@
*/ */
package com.google.android.exoplayer.dash.mpd; package com.google.android.exoplayer.dash.mpd;
import java.util.UUID;
/** /**
* Represents a ContentProtection tag in an AdaptationSet. * Represents a ContentProtection tag in an AdaptationSet.
*/ */
...@@ -26,10 +28,24 @@ public class ContentProtection { ...@@ -26,10 +28,24 @@ public class ContentProtection {
public final String schemeUriId; public final String schemeUriId;
/** /**
* The UUID of the protection scheme. May be null.
*/
public final UUID uuid;
/**
* Protection scheme specific data. May be null.
*/
public final byte[] data;
/**
* @param schemeUriId Identifies the content protection scheme. * @param schemeUriId Identifies the content protection scheme.
* @param uuid The UUID of the protection scheme, if known. May be null.
* @param data Protection scheme specific initialization data. May be null.
*/ */
public ContentProtection(String schemeUriId) { public ContentProtection(String schemeUriId, UUID uuid, byte[] data) {
this.schemeUriId = schemeUriId; this.schemeUriId = schemeUriId;
this.uuid = uuid;
this.data = data;
} }
} }
...@@ -257,7 +257,7 @@ public class MediaPresentationDescriptionParser extends DefaultHandler ...@@ -257,7 +257,7 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
} }
protected ContentProtection buildContentProtection(String schemeIdUri) { protected ContentProtection buildContentProtection(String schemeIdUri) {
return new ContentProtection(schemeIdUri); return new ContentProtection(schemeIdUri, null, null);
} }
/** /**
......
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