Commit 07e3509d by olly Committed by Oliver Woodman

Support providing all keys via EXT-X-SESSION-KEY tags

PiperOrigin-RevId: 240333415
parent c04f5b9c
...@@ -18,6 +18,7 @@ package com.google.android.exoplayer2.drm; ...@@ -18,6 +18,7 @@ package com.google.android.exoplayer2.drm;
import android.os.Parcel; import android.os.Parcel;
import android.os.Parcelable; import android.os.Parcelable;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import android.text.TextUtils;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.drm.DrmInitData.SchemeData; import com.google.android.exoplayer2.drm.DrmInitData.SchemeData;
import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Assertions;
...@@ -183,6 +184,25 @@ public final class DrmInitData implements Comparator<SchemeData>, Parcelable { ...@@ -183,6 +184,25 @@ public final class DrmInitData implements Comparator<SchemeData>, Parcelable {
return new DrmInitData(schemeType, false, schemeDatas); return new DrmInitData(schemeType, false, schemeDatas);
} }
/**
* Returns an instance containing the {@link #schemeDatas} from both this and {@code other}. The
* {@link #schemeType} of the instances being merged must either match, or at least one scheme
* type must be {@code null}.
*
* @param drmInitData The instance to merge.
* @return The merged result.
*/
public DrmInitData merge(DrmInitData drmInitData) {
Assertions.checkState(
schemeType == null
|| drmInitData.schemeType == null
|| TextUtils.equals(schemeType, drmInitData.schemeType));
String mergedSchemeType = schemeType != null ? this.schemeType : drmInitData.schemeType;
SchemeData[] mergedSchemeDatas =
Util.nullSafeArrayConcatenation(schemeDatas, drmInitData.schemeDatas);
return new DrmInitData(mergedSchemeType, mergedSchemeDatas);
}
@Override @Override
public int hashCode() { public int hashCode() {
if (hashCode == 0) { if (hashCode == 0) {
......
...@@ -315,6 +315,25 @@ public final class Util { ...@@ -315,6 +315,25 @@ public final class Util {
} }
/** /**
* Concatenates two non-null type arrays.
*
* @param first The first array.
* @param second The second array.
* @return The concatenated result.
*/
@SuppressWarnings({"nullness:assignment.type.incompatible"})
public static <T> T[] nullSafeArrayConcatenation(T[] first, T[] second) {
T[] concatenation = Arrays.copyOf(first, first.length + second.length);
System.arraycopy(
/* src= */ second,
/* srcPos= */ 0,
/* dest= */ concatenation,
/* destPos= */ first.length,
/* length= */ second.length);
return concatenation;
}
/**
* Creates a {@link Handler} with the specified {@link Handler.Callback} on the current {@link * Creates a {@link Handler} with the specified {@link Handler.Callback} on the current {@link
* Looper} thread. The method accepts partially initialized objects as callback under the * Looper} thread. The method accepts partially initialized objects as callback under the
* assumption that the Handler won't be used to send messages until the callback is fully * assumption that the Handler won't be used to send messages until the callback is fully
......
...@@ -67,6 +67,7 @@ public final class HlsMediaSource extends BaseMediaSource ...@@ -67,6 +67,7 @@ public final class HlsMediaSource extends BaseMediaSource
private CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory; private CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory;
private LoadErrorHandlingPolicy loadErrorHandlingPolicy; private LoadErrorHandlingPolicy loadErrorHandlingPolicy;
private boolean allowChunklessPreparation; private boolean allowChunklessPreparation;
private boolean useSessionKeys;
private boolean isCreateCalled; private boolean isCreateCalled;
@Nullable private Object tag; @Nullable private Object tag;
...@@ -237,6 +238,20 @@ public final class HlsMediaSource extends BaseMediaSource ...@@ -237,6 +238,20 @@ public final class HlsMediaSource extends BaseMediaSource
} }
/** /**
* Sets whether to use #EXT-X-SESSION-KEY tags provided in the master playlist. If enabled, it's
* assumed that any single session key declared in the master playlist can be used to obtain all
* of the keys required for playback. For media where this is not true, this option should not
* be enabled.
*
* @param useSessionKeys Whether to use #EXT-X-SESSION-KEY tags.
* @return This factory, for convenience.
*/
public Factory setUseSessionKeys(boolean useSessionKeys) {
this.useSessionKeys = useSessionKeys;
return this;
}
/**
* Returns a new {@link HlsMediaSource} using the current parameters. * Returns a new {@link HlsMediaSource} using the current parameters.
* *
* @return The new {@link HlsMediaSource}. * @return The new {@link HlsMediaSource}.
...@@ -257,6 +272,7 @@ public final class HlsMediaSource extends BaseMediaSource ...@@ -257,6 +272,7 @@ public final class HlsMediaSource extends BaseMediaSource
playlistTrackerFactory.createTracker( playlistTrackerFactory.createTracker(
hlsDataSourceFactory, loadErrorHandlingPolicy, playlistParserFactory), hlsDataSourceFactory, loadErrorHandlingPolicy, playlistParserFactory),
allowChunklessPreparation, allowChunklessPreparation,
useSessionKeys,
tag); tag);
} }
...@@ -289,6 +305,7 @@ public final class HlsMediaSource extends BaseMediaSource ...@@ -289,6 +305,7 @@ public final class HlsMediaSource extends BaseMediaSource
private final CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory; private final CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory;
private final LoadErrorHandlingPolicy loadErrorHandlingPolicy; private final LoadErrorHandlingPolicy loadErrorHandlingPolicy;
private final boolean allowChunklessPreparation; private final boolean allowChunklessPreparation;
private final boolean useSessionKeys;
private final HlsPlaylistTracker playlistTracker; private final HlsPlaylistTracker playlistTracker;
private final @Nullable Object tag; private final @Nullable Object tag;
...@@ -302,6 +319,7 @@ public final class HlsMediaSource extends BaseMediaSource ...@@ -302,6 +319,7 @@ public final class HlsMediaSource extends BaseMediaSource
LoadErrorHandlingPolicy loadErrorHandlingPolicy, LoadErrorHandlingPolicy loadErrorHandlingPolicy,
HlsPlaylistTracker playlistTracker, HlsPlaylistTracker playlistTracker,
boolean allowChunklessPreparation, boolean allowChunklessPreparation,
boolean useSessionKeys,
@Nullable Object tag) { @Nullable Object tag) {
this.manifestUri = manifestUri; this.manifestUri = manifestUri;
this.dataSourceFactory = dataSourceFactory; this.dataSourceFactory = dataSourceFactory;
...@@ -310,6 +328,7 @@ public final class HlsMediaSource extends BaseMediaSource ...@@ -310,6 +328,7 @@ public final class HlsMediaSource extends BaseMediaSource
this.loadErrorHandlingPolicy = loadErrorHandlingPolicy; this.loadErrorHandlingPolicy = loadErrorHandlingPolicy;
this.playlistTracker = playlistTracker; this.playlistTracker = playlistTracker;
this.allowChunklessPreparation = allowChunklessPreparation; this.allowChunklessPreparation = allowChunklessPreparation;
this.useSessionKeys = useSessionKeys;
this.tag = tag; this.tag = tag;
} }
...@@ -343,7 +362,8 @@ public final class HlsMediaSource extends BaseMediaSource ...@@ -343,7 +362,8 @@ public final class HlsMediaSource extends BaseMediaSource
eventDispatcher, eventDispatcher,
allocator, allocator,
compositeSequenceableLoaderFactory, compositeSequenceableLoaderFactory,
allowChunklessPreparation); allowChunklessPreparation,
useSessionKeys);
} }
@Override @Override
......
...@@ -21,6 +21,7 @@ import com.google.android.exoplayer2.C; ...@@ -21,6 +21,7 @@ import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.FormatHolder; import com.google.android.exoplayer2.FormatHolder;
import com.google.android.exoplayer2.decoder.DecoderInputBuffer; import com.google.android.exoplayer2.decoder.DecoderInputBuffer;
import com.google.android.exoplayer2.drm.DrmInitData;
import com.google.android.exoplayer2.extractor.DummyTrackOutput; import com.google.android.exoplayer2.extractor.DummyTrackOutput;
import com.google.android.exoplayer2.extractor.ExtractorOutput; import com.google.android.exoplayer2.extractor.ExtractorOutput;
import com.google.android.exoplayer2.extractor.SeekMap; import com.google.android.exoplayer2.extractor.SeekMap;
...@@ -52,6 +53,7 @@ import java.util.ArrayList; ...@@ -52,6 +53,7 @@ import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map;
/** /**
* Loads {@link HlsMediaChunk}s obtained from a {@link HlsChunkSource}, and provides * Loads {@link HlsMediaChunk}s obtained from a {@link HlsChunkSource}, and provides
...@@ -102,6 +104,7 @@ import java.util.List; ...@@ -102,6 +104,7 @@ import java.util.List;
private final Runnable onTracksEndedRunnable; private final Runnable onTracksEndedRunnable;
private final Handler handler; private final Handler handler;
private final ArrayList<HlsSampleStream> hlsSampleStreams; private final ArrayList<HlsSampleStream> hlsSampleStreams;
private final Map<String, DrmInitData> overridingDrmInitData;
private SampleQueue[] sampleQueues; private SampleQueue[] sampleQueues;
private int[] sampleQueueTrackIds; private int[] sampleQueueTrackIds;
...@@ -144,6 +147,10 @@ import java.util.List; ...@@ -144,6 +147,10 @@ import java.util.List;
* @param trackType The type of the track. One of the {@link C} {@code TRACK_TYPE_*} constants. * @param trackType The type of the track. One of the {@link C} {@code TRACK_TYPE_*} constants.
* @param callback A callback for the wrapper. * @param callback A callback for the wrapper.
* @param chunkSource A {@link HlsChunkSource} from which chunks to load are obtained. * @param chunkSource A {@link HlsChunkSource} from which chunks to load are obtained.
* @param overridingDrmInitData Overriding {@link DrmInitData}, keyed by protection scheme type
* (i.e. {@link DrmInitData#schemeType}). If the stream has {@link DrmInitData} and uses a
* protection scheme type for which overriding {@link DrmInitData} is provided, then the
* stream's {@link DrmInitData} will be overridden.
* @param allocator An {@link Allocator} from which to obtain media buffer allocations. * @param allocator An {@link Allocator} from which to obtain media buffer allocations.
* @param positionUs The position from which to start loading media. * @param positionUs The position from which to start loading media.
* @param muxedAudioFormat Optional muxed audio {@link Format} as defined by the master playlist. * @param muxedAudioFormat Optional muxed audio {@link Format} as defined by the master playlist.
...@@ -154,6 +161,7 @@ import java.util.List; ...@@ -154,6 +161,7 @@ import java.util.List;
int trackType, int trackType,
Callback callback, Callback callback,
HlsChunkSource chunkSource, HlsChunkSource chunkSource,
Map<String, DrmInitData> overridingDrmInitData,
Allocator allocator, Allocator allocator,
long positionUs, long positionUs,
Format muxedAudioFormat, Format muxedAudioFormat,
...@@ -162,6 +170,7 @@ import java.util.List; ...@@ -162,6 +170,7 @@ import java.util.List;
this.trackType = trackType; this.trackType = trackType;
this.callback = callback; this.callback = callback;
this.chunkSource = chunkSource; this.chunkSource = chunkSource;
this.overridingDrmInitData = overridingDrmInitData;
this.allocator = allocator; this.allocator = allocator;
this.muxedAudioFormat = muxedAudioFormat; this.muxedAudioFormat = muxedAudioFormat;
this.loadErrorHandlingPolicy = loadErrorHandlingPolicy; this.loadErrorHandlingPolicy = loadErrorHandlingPolicy;
...@@ -484,7 +493,9 @@ import java.util.List; ...@@ -484,7 +493,9 @@ import java.util.List;
int result = int result =
sampleQueues[sampleQueueIndex].read( sampleQueues[sampleQueueIndex].read(
formatHolder, buffer, requireFormat, loadingFinished, lastSeekPositionUs); formatHolder, buffer, requireFormat, loadingFinished, lastSeekPositionUs);
if (result == C.RESULT_FORMAT_READ && sampleQueueIndex == primarySampleQueueIndex) { if (result == C.RESULT_FORMAT_READ) {
Format format = formatHolder.format;
if (sampleQueueIndex == primarySampleQueueIndex) {
// Fill in primary sample format with information from the track format. // Fill in primary sample format with information from the track format.
int chunkUid = sampleQueues[sampleQueueIndex].peekSourceId(); int chunkUid = sampleQueues[sampleQueueIndex].peekSourceId();
int chunkIndex = 0; int chunkIndex = 0;
...@@ -495,7 +506,15 @@ import java.util.List; ...@@ -495,7 +506,15 @@ import java.util.List;
chunkIndex < mediaChunks.size() chunkIndex < mediaChunks.size()
? mediaChunks.get(chunkIndex).trackFormat ? mediaChunks.get(chunkIndex).trackFormat
: upstreamTrackFormat; : upstreamTrackFormat;
formatHolder.format = formatHolder.format.copyWithManifestFormatInfo(trackFormat); format = format.copyWithManifestFormatInfo(trackFormat);
}
if (format.drmInitData != null) {
DrmInitData drmInitData = overridingDrmInitData.get(format.drmInitData.schemeType);
if (drmInitData != null) {
format = format.copyWithDrmInitData(drmInitData);
}
}
formatHolder.format = format;
} }
return result; return result;
} }
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
package com.google.android.exoplayer2.source.hls.playlist; package com.google.android.exoplayer2.source.hls.playlist;
import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.drm.DrmInitData;
import com.google.android.exoplayer2.offline.StreamKey; import com.google.android.exoplayer2.offline.StreamKey;
import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.MimeTypes;
import java.util.ArrayList; import java.util.ArrayList;
...@@ -37,7 +38,8 @@ public final class HlsMasterPlaylist extends HlsPlaylist { ...@@ -37,7 +38,8 @@ public final class HlsMasterPlaylist extends HlsPlaylist {
/* muxedAudioFormat= */ null, /* muxedAudioFormat= */ null,
/* muxedCaptionFormats= */ Collections.emptyList(), /* muxedCaptionFormats= */ Collections.emptyList(),
/* hasIndependentSegments= */ false, /* hasIndependentSegments= */ false,
/* variableDefinitions= */ Collections.emptyMap()); /* variableDefinitions= */ Collections.emptyMap(),
/* sessionKeyDrmInitData= */ Collections.emptyList());
public static final int GROUP_INDEX_VARIANT = 0; public static final int GROUP_INDEX_VARIANT = 0;
public static final int GROUP_INDEX_AUDIO = 1; public static final int GROUP_INDEX_AUDIO = 1;
...@@ -122,6 +124,8 @@ public final class HlsMasterPlaylist extends HlsPlaylist { ...@@ -122,6 +124,8 @@ public final class HlsMasterPlaylist extends HlsPlaylist {
public final List<Format> muxedCaptionFormats; public final List<Format> muxedCaptionFormats;
/** Contains variable definitions, as defined by the #EXT-X-DEFINE tag. */ /** Contains variable definitions, as defined by the #EXT-X-DEFINE tag. */
public final Map<String, String> variableDefinitions; public final Map<String, String> variableDefinitions;
/** DRM initialization data derived from #EXT-X-SESSION-KEY tags. */
public final List<DrmInitData> sessionKeyDrmInitData;
/** /**
* @param baseUri See {@link #baseUri}. * @param baseUri See {@link #baseUri}.
...@@ -133,6 +137,7 @@ public final class HlsMasterPlaylist extends HlsPlaylist { ...@@ -133,6 +137,7 @@ public final class HlsMasterPlaylist extends HlsPlaylist {
* @param muxedCaptionFormats See {@link #muxedCaptionFormats}. * @param muxedCaptionFormats See {@link #muxedCaptionFormats}.
* @param hasIndependentSegments See {@link #hasIndependentSegments}. * @param hasIndependentSegments See {@link #hasIndependentSegments}.
* @param variableDefinitions See {@link #variableDefinitions}. * @param variableDefinitions See {@link #variableDefinitions}.
* @param sessionKeyDrmInitData See {@link #sessionKeyDrmInitData}.
*/ */
public HlsMasterPlaylist( public HlsMasterPlaylist(
String baseUri, String baseUri,
...@@ -143,7 +148,8 @@ public final class HlsMasterPlaylist extends HlsPlaylist { ...@@ -143,7 +148,8 @@ public final class HlsMasterPlaylist extends HlsPlaylist {
Format muxedAudioFormat, Format muxedAudioFormat,
List<Format> muxedCaptionFormats, List<Format> muxedCaptionFormats,
boolean hasIndependentSegments, boolean hasIndependentSegments,
Map<String, String> variableDefinitions) { Map<String, String> variableDefinitions,
List<DrmInitData> sessionKeyDrmInitData) {
super(baseUri, tags, hasIndependentSegments); super(baseUri, tags, hasIndependentSegments);
this.variants = Collections.unmodifiableList(variants); this.variants = Collections.unmodifiableList(variants);
this.audios = Collections.unmodifiableList(audios); this.audios = Collections.unmodifiableList(audios);
...@@ -152,6 +158,7 @@ public final class HlsMasterPlaylist extends HlsPlaylist { ...@@ -152,6 +158,7 @@ public final class HlsMasterPlaylist extends HlsPlaylist {
this.muxedCaptionFormats = muxedCaptionFormats != null this.muxedCaptionFormats = muxedCaptionFormats != null
? Collections.unmodifiableList(muxedCaptionFormats) : null; ? Collections.unmodifiableList(muxedCaptionFormats) : null;
this.variableDefinitions = Collections.unmodifiableMap(variableDefinitions); this.variableDefinitions = Collections.unmodifiableMap(variableDefinitions);
this.sessionKeyDrmInitData = Collections.unmodifiableList(sessionKeyDrmInitData);
} }
@Override @Override
...@@ -165,7 +172,8 @@ public final class HlsMasterPlaylist extends HlsPlaylist { ...@@ -165,7 +172,8 @@ public final class HlsMasterPlaylist extends HlsPlaylist {
muxedAudioFormat, muxedAudioFormat,
muxedCaptionFormats, muxedCaptionFormats,
hasIndependentSegments, hasIndependentSegments,
variableDefinitions); variableDefinitions,
sessionKeyDrmInitData);
} }
/** /**
...@@ -186,7 +194,8 @@ public final class HlsMasterPlaylist extends HlsPlaylist { ...@@ -186,7 +194,8 @@ public final class HlsMasterPlaylist extends HlsPlaylist {
/* muxedAudioFormat= */ null, /* muxedAudioFormat= */ null,
/* muxedCaptionFormats= */ null, /* muxedCaptionFormats= */ null,
/* hasIndependentSegments= */ false, /* hasIndependentSegments= */ false,
/* variableDefinitions= */ Collections.emptyMap()); /* variableDefinitions= */ Collections.emptyMap(),
/* sessionKeyDrmInitData= */ Collections.emptyList());
} }
private static List<HlsUrl> copyRenditionsList( private static List<HlsUrl> copyRenditionsList(
......
...@@ -34,7 +34,6 @@ import java.io.BufferedReader; ...@@ -34,7 +34,6 @@ import java.io.BufferedReader;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.util.ArrayDeque; import java.util.ArrayDeque;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
...@@ -73,6 +72,7 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli ...@@ -73,6 +72,7 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
private static final String TAG_START = "#EXT-X-START"; private static final String TAG_START = "#EXT-X-START";
private static final String TAG_ENDLIST = "#EXT-X-ENDLIST"; private static final String TAG_ENDLIST = "#EXT-X-ENDLIST";
private static final String TAG_KEY = "#EXT-X-KEY"; private static final String TAG_KEY = "#EXT-X-KEY";
private static final String TAG_SESSION_KEY = "#EXT-X-SESSION-KEY";
private static final String TAG_BYTERANGE = "#EXT-X-BYTERANGE"; private static final String TAG_BYTERANGE = "#EXT-X-BYTERANGE";
private static final String TAG_GAP = "#EXT-X-GAP"; private static final String TAG_GAP = "#EXT-X-GAP";
...@@ -253,6 +253,7 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli ...@@ -253,6 +253,7 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
ArrayList<HlsMasterPlaylist.HlsUrl> audios = new ArrayList<>(); ArrayList<HlsMasterPlaylist.HlsUrl> audios = new ArrayList<>();
ArrayList<HlsMasterPlaylist.HlsUrl> subtitles = new ArrayList<>(); ArrayList<HlsMasterPlaylist.HlsUrl> subtitles = new ArrayList<>();
ArrayList<String> mediaTags = new ArrayList<>(); ArrayList<String> mediaTags = new ArrayList<>();
ArrayList<DrmInitData> sessionKeyDrmInitData = new ArrayList<>();
ArrayList<String> tags = new ArrayList<>(); ArrayList<String> tags = new ArrayList<>();
Format muxedAudioFormat = null; Format muxedAudioFormat = null;
List<Format> muxedCaptionFormats = null; List<Format> muxedCaptionFormats = null;
...@@ -278,6 +279,15 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli ...@@ -278,6 +279,15 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
// Media tags are parsed at the end to include codec information from #EXT-X-STREAM-INF // Media tags are parsed at the end to include codec information from #EXT-X-STREAM-INF
// tags. // tags.
mediaTags.add(line); mediaTags.add(line);
} else if (line.startsWith(TAG_SESSION_KEY)) {
String keyFormat =
parseOptionalStringAttr(line, REGEX_KEYFORMAT, KEYFORMAT_IDENTITY, variableDefinitions);
SchemeData schemeData = parseDrmSchemeData(line, keyFormat, variableDefinitions);
if (schemeData != null) {
String method = parseStringAttr(line, REGEX_METHOD, variableDefinitions);
String scheme = parseEncryptionScheme(method);
sessionKeyDrmInitData.add(new DrmInitData(scheme, schemeData));
}
} else if (line.startsWith(TAG_STREAM_INF)) { } else if (line.startsWith(TAG_STREAM_INF)) {
noClosedCaptions |= line.contains(ATTR_CLOSED_CAPTIONS_NONE); noClosedCaptions |= line.contains(ATTR_CLOSED_CAPTIONS_NONE);
int bitrate = parseIntAttr(line, REGEX_BANDWIDTH); int bitrate = parseIntAttr(line, REGEX_BANDWIDTH);
...@@ -423,6 +433,7 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli ...@@ -423,6 +433,7 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
if (noClosedCaptions) { if (noClosedCaptions) {
muxedCaptionFormats = Collections.emptyList(); muxedCaptionFormats = Collections.emptyList();
} }
return new HlsMasterPlaylist( return new HlsMasterPlaylist(
baseUri, baseUri,
tags, tags,
...@@ -432,7 +443,8 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli ...@@ -432,7 +443,8 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
muxedAudioFormat, muxedAudioFormat,
muxedCaptionFormats, muxedCaptionFormats,
hasIndependentSegmentsTag, hasIndependentSegmentsTag,
variableDefinitions); variableDefinitions,
sessionKeyDrmInitData);
} }
private static HlsMediaPlaylist parseMediaPlaylist( private static HlsMediaPlaylist parseMediaPlaylist(
...@@ -557,17 +569,9 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli ...@@ -557,17 +569,9 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
} }
} else { } else {
if (encryptionScheme == null) { if (encryptionScheme == null) {
encryptionScheme = encryptionScheme = parseEncryptionScheme(method);
METHOD_SAMPLE_AES_CENC.equals(method) || METHOD_SAMPLE_AES_CTR.equals(method)
? C.CENC_TYPE_cenc
: C.CENC_TYPE_cbcs;
}
SchemeData schemeData;
if (KEYFORMAT_PLAYREADY.equals(keyFormat)) {
schemeData = parsePlayReadySchemeData(line, variableDefinitions);
} else {
schemeData = parseWidevineSchemeData(line, keyFormat, variableDefinitions);
} }
SchemeData schemeData = parseDrmSchemeData(line, keyFormat, variableDefinitions);
if (schemeData != null) { if (schemeData != null) {
cachedDrmInitData = null; cachedDrmInitData = null;
currentSchemeDatas.put(keyFormat, schemeData); currentSchemeDatas.put(keyFormat, schemeData);
...@@ -713,40 +717,35 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli ...@@ -713,40 +717,35 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
: Format.NO_VALUE; : Format.NO_VALUE;
} }
private static @Nullable SchemeData parsePlayReadySchemeData( @Nullable
String line, Map<String, String> variableDefinitions) throws ParserException { private static SchemeData parseDrmSchemeData(
String keyFormatVersions =
parseOptionalStringAttr(line, REGEX_KEYFORMATVERSIONS, "1", variableDefinitions);
if (!"1".equals(keyFormatVersions)) {
// Not supported.
return null;
}
String uriString = parseStringAttr(line, REGEX_URI, variableDefinitions);
byte[] data = Base64.decode(uriString.substring(uriString.indexOf(',')), Base64.DEFAULT);
byte[] psshData = PsshAtomUtil.buildPsshAtom(C.PLAYREADY_UUID, data);
return new SchemeData(C.PLAYREADY_UUID, MimeTypes.VIDEO_MP4, psshData);
}
private static @Nullable SchemeData parseWidevineSchemeData(
String line, String keyFormat, Map<String, String> variableDefinitions) String line, String keyFormat, Map<String, String> variableDefinitions)
throws ParserException { throws ParserException {
String keyFormatVersions =
parseOptionalStringAttr(line, REGEX_KEYFORMATVERSIONS, "1", variableDefinitions);
if (KEYFORMAT_WIDEVINE_PSSH_BINARY.equals(keyFormat)) { if (KEYFORMAT_WIDEVINE_PSSH_BINARY.equals(keyFormat)) {
String uriString = parseStringAttr(line, REGEX_URI, variableDefinitions); String uriString = parseStringAttr(line, REGEX_URI, variableDefinitions);
return new SchemeData( return new SchemeData(
C.WIDEVINE_UUID, C.WIDEVINE_UUID,
MimeTypes.VIDEO_MP4, MimeTypes.VIDEO_MP4,
Base64.decode(uriString.substring(uriString.indexOf(',')), Base64.DEFAULT)); Base64.decode(uriString.substring(uriString.indexOf(',')), Base64.DEFAULT));
} } else if (KEYFORMAT_WIDEVINE_PSSH_JSON.equals(keyFormat)) {
if (KEYFORMAT_WIDEVINE_PSSH_JSON.equals(keyFormat)) { return new SchemeData(C.WIDEVINE_UUID, "hls", Util.getUtf8Bytes(line));
try { } else if (KEYFORMAT_PLAYREADY.equals(keyFormat) && "1".equals(keyFormatVersions)) {
return new SchemeData(C.WIDEVINE_UUID, "hls", line.getBytes(C.UTF8_NAME)); String uriString = parseStringAttr(line, REGEX_URI, variableDefinitions);
} catch (UnsupportedEncodingException e) { byte[] data = Base64.decode(uriString.substring(uriString.indexOf(',')), Base64.DEFAULT);
throw new ParserException(e); byte[] psshData = PsshAtomUtil.buildPsshAtom(C.PLAYREADY_UUID, data);
} return new SchemeData(C.PLAYREADY_UUID, MimeTypes.VIDEO_MP4, psshData);
} }
return null; return null;
} }
private static String parseEncryptionScheme(String method) {
return METHOD_SAMPLE_AES_CENC.equals(method) || METHOD_SAMPLE_AES_CTR.equals(method)
? C.CENC_TYPE_cenc
: C.CENC_TYPE_cbcs;
}
private static int parseIntAttr(String line, Pattern pattern) throws ParserException { private static int parseIntAttr(String line, Pattern pattern) throws ParserException {
return Integer.parseInt(parseStringAttr(line, pattern, Collections.emptyMap())); return Integer.parseInt(parseStringAttr(line, pattern, Collections.emptyMap()));
} }
......
...@@ -88,7 +88,8 @@ public final class HlsMediaPeriodTest { ...@@ -88,7 +88,8 @@ public final class HlsMediaPeriodTest {
/* mediaTimeOffsetMs= */ 0), /* mediaTimeOffsetMs= */ 0),
mock(Allocator.class), mock(Allocator.class),
mock(CompositeSequenceableLoaderFactory.class), mock(CompositeSequenceableLoaderFactory.class),
/* allowChunklessPreparation =*/ true); /* allowChunklessPreparation =*/ true,
/* useSessionKeys= */ false);
}; };
MediaPeriodAsserts.assertGetStreamKeysAndManifestFilterIntegration( MediaPeriodAsserts.assertGetStreamKeysAndManifestFilterIntegration(
...@@ -110,7 +111,8 @@ public final class HlsMediaPeriodTest { ...@@ -110,7 +111,8 @@ public final class HlsMediaPeriodTest {
muxedAudioFormat, muxedAudioFormat,
muxedCaptionFormats, muxedCaptionFormats,
/* hasIndependentSegments= */ true, /* hasIndependentSegments= */ true,
/* variableDefinitions= */ Collections.emptyMap()); /* variableDefinitions= */ Collections.emptyMap(),
/* sessionKeyDrmInitData= */ Collections.emptyList());
} }
private static HlsUrl createMuxedVideoAudioVariantHlsUrl(int bitrate) { private static HlsUrl createMuxedVideoAudioVariantHlsUrl(int bitrate) {
......
...@@ -456,7 +456,8 @@ public class HlsMediaPlaylistParserTest { ...@@ -456,7 +456,8 @@ public class HlsMediaPlaylistParserTest {
/* muxedAudioFormat= */ null, /* muxedAudioFormat= */ null,
/* muxedCaptionFormats= */ null, /* muxedCaptionFormats= */ null,
/* hasIndependentSegments= */ true, /* hasIndependentSegments= */ true,
/* variableDefinitions */ Collections.emptyMap()); /* variableDefinitions= */ Collections.emptyMap(),
/* sessionKeyDrmInitData= */ Collections.emptyList());
HlsMediaPlaylist playlistWithInheritance = HlsMediaPlaylist playlistWithInheritance =
(HlsMediaPlaylist) new HlsPlaylistParser(masterPlaylist).parse(playlistUri, inputStream); (HlsMediaPlaylist) new HlsPlaylistParser(masterPlaylist).parse(playlistUri, inputStream);
assertThat(playlistWithInheritance.hasIndependentSegments).isTrue(); assertThat(playlistWithInheritance.hasIndependentSegments).isTrue();
...@@ -515,7 +516,8 @@ public class HlsMediaPlaylistParserTest { ...@@ -515,7 +516,8 @@ public class HlsMediaPlaylistParserTest {
/* muxedAudioFormat= */ null, /* muxedAudioFormat= */ null,
/* muxedCaptionFormats= */ Collections.emptyList(), /* muxedCaptionFormats= */ Collections.emptyList(),
/* hasIndependentSegments= */ false, /* hasIndependentSegments= */ false,
variableDefinitions); variableDefinitions,
/* sessionKeyDrmInitData= */ Collections.emptyList());
HlsMediaPlaylist playlist = HlsMediaPlaylist playlist =
(HlsMediaPlaylist) new HlsPlaylistParser(masterPlaylist).parse(playlistUri, inputStream); (HlsMediaPlaylist) new HlsPlaylistParser(masterPlaylist).parse(playlistUri, inputStream);
for (int i = 1; i <= 4; i++) { for (int i = 1; i <= 4; i++) {
......
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