Skip to content
Toggle navigation
P
Projects
G
Groups
S
Snippets
Help
SDK
/
exoplayer
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
0
Merge Requests
0
Pipelines
Wiki
Snippets
Settings
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Commit
07e3509d
authored
Mar 26, 2019
by
olly
Committed by
Oliver Woodman
Mar 26, 2019
Browse files
Options
_('Browse Files')
Download
Email Patches
Plain Diff
Support providing all keys via EXT-X-SESSION-KEY tags
PiperOrigin-RevId: 240333415
parent
c04f5b9c
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
217 additions
and
64 deletions
library/core/src/main/java/com/google/android/exoplayer2/drm/DrmInitData.java
library/core/src/main/java/com/google/android/exoplayer2/util/Util.java
library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaPeriod.java
library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaSource.java
library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsSampleStreamWrapper.java
library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsMasterPlaylist.java
library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistParser.java
library/hls/src/test/java/com/google/android/exoplayer2/source/hls/HlsMediaPeriodTest.java
library/hls/src/test/java/com/google/android/exoplayer2/source/hls/playlist/HlsMediaPlaylistParserTest.java
library/core/src/main/java/com/google/android/exoplayer2/drm/DrmInitData.java
View file @
07e3509d
...
...
@@ -18,6 +18,7 @@ package com.google.android.exoplayer2.drm;
import
android.os.Parcel
;
import
android.os.Parcelable
;
import
androidx.annotation.Nullable
;
import
android.text.TextUtils
;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.drm.DrmInitData.SchemeData
;
import
com.google.android.exoplayer2.util.Assertions
;
...
...
@@ -183,6 +184,25 @@ public final class DrmInitData implements Comparator<SchemeData>, Parcelable {
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
public
int
hashCode
()
{
if
(
hashCode
==
0
)
{
...
...
library/core/src/main/java/com/google/android/exoplayer2/util/Util.java
View file @
07e3509d
...
...
@@ -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
* 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
...
...
library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaPeriod.java
View file @
07e3509d
...
...
@@ -16,9 +16,11 @@
package
com
.
google
.
android
.
exoplayer2
.
source
.
hls
;
import
androidx.annotation.Nullable
;
import
android.text.TextUtils
;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.Format
;
import
com.google.android.exoplayer2.SeekParameters
;
import
com.google.android.exoplayer2.drm.DrmInitData
;
import
com.google.android.exoplayer2.extractor.Extractor
;
import
com.google.android.exoplayer2.offline.StreamKey
;
import
com.google.android.exoplayer2.source.CompositeSequenceableLoaderFactory
;
...
...
@@ -43,9 +45,11 @@ import java.io.IOException;
import
java.util.ArrayList
;
import
java.util.Arrays
;
import
java.util.Collections
;
import
java.util.HashMap
;
import
java.util.HashSet
;
import
java.util.IdentityHashMap
;
import
java.util.List
;
import
java.util.Map
;
/**
* A {@link MediaPeriod} that loads an HLS stream.
...
...
@@ -64,6 +68,7 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsSampleStreamWrapper
private
final
TimestampAdjusterProvider
timestampAdjusterProvider
;
private
final
CompositeSequenceableLoaderFactory
compositeSequenceableLoaderFactory
;
private
final
boolean
allowChunklessPreparation
;
private
final
boolean
useSessionKeys
;
private
@Nullable
Callback
callback
;
private
int
pendingPrepareCount
;
...
...
@@ -90,6 +95,7 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsSampleStreamWrapper
* @param compositeSequenceableLoaderFactory A factory to create composite {@link
* SequenceableLoader}s for when this media source loads data from multiple streams.
* @param allowChunklessPreparation Whether chunkless preparation is allowed.
* @param useSessionKeys Whether to use #EXT-X-SESSION-KEY tags.
*/
public
HlsMediaPeriod
(
HlsExtractorFactory
extractorFactory
,
...
...
@@ -100,7 +106,8 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsSampleStreamWrapper
EventDispatcher
eventDispatcher
,
Allocator
allocator
,
CompositeSequenceableLoaderFactory
compositeSequenceableLoaderFactory
,
boolean
allowChunklessPreparation
)
{
boolean
allowChunklessPreparation
,
boolean
useSessionKeys
)
{
this
.
extractorFactory
=
extractorFactory
;
this
.
playlistTracker
=
playlistTracker
;
this
.
dataSourceFactory
=
dataSourceFactory
;
...
...
@@ -110,6 +117,7 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsSampleStreamWrapper
this
.
allocator
=
allocator
;
this
.
compositeSequenceableLoaderFactory
=
compositeSequenceableLoaderFactory
;
this
.
allowChunklessPreparation
=
allowChunklessPreparation
;
this
.
useSessionKeys
=
useSessionKeys
;
compositeSequenceableLoader
=
compositeSequenceableLoaderFactory
.
createCompositeSequenceableLoader
();
streamWrapperIndices
=
new
IdentityHashMap
<>();
...
...
@@ -427,7 +435,12 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsSampleStreamWrapper
// Internal methods.
private
void
buildAndPrepareSampleStreamWrappers
(
long
positionUs
)
{
HlsMasterPlaylist
masterPlaylist
=
playlistTracker
.
getMasterPlaylist
();
HlsMasterPlaylist
masterPlaylist
=
Assertions
.
checkNotNull
(
playlistTracker
.
getMasterPlaylist
());
Map
<
String
,
DrmInitData
>
overridingDrmInitData
=
useSessionKeys
?
deriveOverridingDrmInitData
(
masterPlaylist
.
sessionKeyDrmInitData
)
:
Collections
.
emptyMap
();
boolean
hasVariants
=
!
masterPlaylist
.
variants
.
isEmpty
();
List
<
HlsUrl
>
audioRenditions
=
masterPlaylist
.
audios
;
List
<
HlsUrl
>
subtitleRenditions
=
masterPlaylist
.
subtitles
;
...
...
@@ -438,20 +451,33 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsSampleStreamWrapper
if
(
hasVariants
)
{
buildAndPrepareMainSampleStreamWrapper
(
masterPlaylist
,
positionUs
,
sampleStreamWrappers
,
manifestUrlIndicesPerWrapper
);
masterPlaylist
,
positionUs
,
sampleStreamWrappers
,
manifestUrlIndicesPerWrapper
,
overridingDrmInitData
);
}
// TODO: Build video stream wrappers here.
buildAndPrepareAudioSampleStreamWrappers
(
positionUs
,
audioRenditions
,
sampleStreamWrappers
,
manifestUrlIndicesPerWrapper
);
positionUs
,
audioRenditions
,
sampleStreamWrappers
,
manifestUrlIndicesPerWrapper
,
overridingDrmInitData
);
// Subtitle stream wrappers. We can always use master playlist information to prepare these.
for
(
int
i
=
0
;
i
<
subtitleRenditions
.
size
();
i
++)
{
HlsUrl
url
=
subtitleRenditions
.
get
(
i
);
HlsSampleStreamWrapper
sampleStreamWrapper
=
buildSampleStreamWrapper
(
C
.
TRACK_TYPE_TEXT
,
new
HlsUrl
[]
{
url
},
null
,
Collections
.
emptyList
(),
positionUs
);
C
.
TRACK_TYPE_TEXT
,
new
HlsUrl
[]
{
url
},
null
,
Collections
.
emptyList
(),
overridingDrmInitData
,
positionUs
);
manifestUrlIndicesPerWrapper
.
add
(
new
int
[]
{
i
});
sampleStreamWrappers
.
add
(
sampleStreamWrapper
);
sampleStreamWrapper
.
prepareWithMasterPlaylistInfo
(
...
...
@@ -495,12 +521,15 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsSampleStreamWrapper
* which downloading should start. Ignored otherwise.
* @param sampleStreamWrappers List to which the built main sample stream wrapper should be added.
* @param manifestUrlIndicesPerWrapper List to which the selected variant indices should be added.
* @param overridingDrmInitData Overriding {@link DrmInitData}, keyed by protection scheme type
* (i.e. {@link DrmInitData#schemeType}).
*/
private
void
buildAndPrepareMainSampleStreamWrapper
(
HlsMasterPlaylist
masterPlaylist
,
long
positionUs
,
List
<
HlsSampleStreamWrapper
>
sampleStreamWrappers
,
List
<
int
[]>
manifestUrlIndicesPerWrapper
)
{
List
<
int
[]>
manifestUrlIndicesPerWrapper
,
Map
<
String
,
DrmInitData
>
overridingDrmInitData
)
{
int
[]
variantTypes
=
new
int
[
masterPlaylist
.
variants
.
size
()];
int
videoVariantCount
=
0
;
int
audioVariantCount
=
0
;
...
...
@@ -549,6 +578,7 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsSampleStreamWrapper
selectedVariants
,
masterPlaylist
.
muxedAudioFormat
,
masterPlaylist
.
muxedCaptionFormats
,
overridingDrmInitData
,
positionUs
);
sampleStreamWrappers
.
add
(
sampleStreamWrapper
);
manifestUrlIndicesPerWrapper
.
add
(
selectedVariantIndices
);
...
...
@@ -616,8 +646,8 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsSampleStreamWrapper
long
positionUs
,
List
<
HlsUrl
>
audioRenditions
,
List
<
HlsSampleStreamWrapper
>
sampleStreamWrappers
,
List
<
int
[]>
manifestUrlsIndicesPerWrapper
)
{
List
<
int
[]>
manifestUrlsIndicesPerWrapper
,
Map
<
String
,
DrmInitData
>
overridingDrmInitData
)
{
ArrayList
<
HlsUrl
>
scratchRenditionList
=
new
ArrayList
<>(
/* initialCapacity= */
audioRenditions
.
size
());
ArrayList
<
Integer
>
scratchIndicesList
=
...
...
@@ -651,6 +681,7 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsSampleStreamWrapper
scratchRenditionList
.
toArray
(
new
HlsUrl
[
0
]),
/* muxedAudioFormat= */
null
,
/* muxedCaptionFormats= */
Collections
.
emptyList
(),
overridingDrmInitData
,
positionUs
);
manifestUrlsIndicesPerWrapper
.
add
(
Util
.
toArray
(
scratchIndicesList
));
sampleStreamWrappers
.
add
(
sampleStreamWrapper
);
...
...
@@ -666,8 +697,13 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsSampleStreamWrapper
}
}
private
HlsSampleStreamWrapper
buildSampleStreamWrapper
(
int
trackType
,
HlsUrl
[]
variants
,
Format
muxedAudioFormat
,
List
<
Format
>
muxedCaptionFormats
,
long
positionUs
)
{
private
HlsSampleStreamWrapper
buildSampleStreamWrapper
(
int
trackType
,
HlsUrl
[]
variants
,
Format
muxedAudioFormat
,
List
<
Format
>
muxedCaptionFormats
,
Map
<
String
,
DrmInitData
>
overridingDrmInitData
,
long
positionUs
)
{
HlsChunkSource
defaultChunkSource
=
new
HlsChunkSource
(
extractorFactory
,
...
...
@@ -681,6 +717,7 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsSampleStreamWrapper
trackType
,
/* callback= */
this
,
defaultChunkSource
,
overridingDrmInitData
,
allocator
,
positionUs
,
muxedAudioFormat
,
...
...
@@ -688,6 +725,32 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsSampleStreamWrapper
eventDispatcher
);
}
private
static
Map
<
String
,
DrmInitData
>
deriveOverridingDrmInitData
(
List
<
DrmInitData
>
sessionKeyDrmInitData
)
{
ArrayList
<
DrmInitData
>
mutableSessionKeyDrmInitData
=
new
ArrayList
<>(
sessionKeyDrmInitData
);
HashMap
<
String
,
DrmInitData
>
drmInitDataBySchemeType
=
new
HashMap
<>();
for
(
int
i
=
0
;
i
<
mutableSessionKeyDrmInitData
.
size
();
i
++)
{
DrmInitData
drmInitData
=
sessionKeyDrmInitData
.
get
(
i
);
String
scheme
=
drmInitData
.
schemeType
;
// Merge any subsequent drmInitData instances that have the same scheme type. This is valid
// due to the assumptions documented on HlsMediaSource.Builder.setUseSessionKeys, and is
// necessary to get data for different CDNs (e.g. Widevine and PlayReady) into a single
// drmInitData.
int
j
=
i
+
1
;
while
(
j
<
mutableSessionKeyDrmInitData
.
size
())
{
DrmInitData
nextDrmInitData
=
mutableSessionKeyDrmInitData
.
get
(
j
);
if
(
TextUtils
.
equals
(
nextDrmInitData
.
schemeType
,
scheme
))
{
drmInitData
=
drmInitData
.
merge
(
nextDrmInitData
);
mutableSessionKeyDrmInitData
.
remove
(
j
);
}
else
{
j
++;
}
}
drmInitDataBySchemeType
.
put
(
scheme
,
drmInitData
);
}
return
drmInitDataBySchemeType
;
}
private
static
Format
deriveVideoFormat
(
Format
variantFormat
)
{
String
codecs
=
Util
.
getCodecsOfType
(
variantFormat
.
codecs
,
C
.
TRACK_TYPE_VIDEO
);
String
sampleMimeType
=
MimeTypes
.
getMediaMimeType
(
codecs
);
...
...
library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaSource.java
View file @
07e3509d
...
...
@@ -67,6 +67,7 @@ public final class HlsMediaSource extends BaseMediaSource
private
CompositeSequenceableLoaderFactory
compositeSequenceableLoaderFactory
;
private
LoadErrorHandlingPolicy
loadErrorHandlingPolicy
;
private
boolean
allowChunklessPreparation
;
private
boolean
useSessionKeys
;
private
boolean
isCreateCalled
;
@Nullable
private
Object
tag
;
...
...
@@ -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.
*
* @return The new {@link HlsMediaSource}.
...
...
@@ -257,6 +272,7 @@ public final class HlsMediaSource extends BaseMediaSource
playlistTrackerFactory
.
createTracker
(
hlsDataSourceFactory
,
loadErrorHandlingPolicy
,
playlistParserFactory
),
allowChunklessPreparation
,
useSessionKeys
,
tag
);
}
...
...
@@ -289,6 +305,7 @@ public final class HlsMediaSource extends BaseMediaSource
private
final
CompositeSequenceableLoaderFactory
compositeSequenceableLoaderFactory
;
private
final
LoadErrorHandlingPolicy
loadErrorHandlingPolicy
;
private
final
boolean
allowChunklessPreparation
;
private
final
boolean
useSessionKeys
;
private
final
HlsPlaylistTracker
playlistTracker
;
private
final
@Nullable
Object
tag
;
...
...
@@ -302,6 +319,7 @@ public final class HlsMediaSource extends BaseMediaSource
LoadErrorHandlingPolicy
loadErrorHandlingPolicy
,
HlsPlaylistTracker
playlistTracker
,
boolean
allowChunklessPreparation
,
boolean
useSessionKeys
,
@Nullable
Object
tag
)
{
this
.
manifestUri
=
manifestUri
;
this
.
dataSourceFactory
=
dataSourceFactory
;
...
...
@@ -310,6 +328,7 @@ public final class HlsMediaSource extends BaseMediaSource
this
.
loadErrorHandlingPolicy
=
loadErrorHandlingPolicy
;
this
.
playlistTracker
=
playlistTracker
;
this
.
allowChunklessPreparation
=
allowChunklessPreparation
;
this
.
useSessionKeys
=
useSessionKeys
;
this
.
tag
=
tag
;
}
...
...
@@ -343,7 +362,8 @@ public final class HlsMediaSource extends BaseMediaSource
eventDispatcher
,
allocator
,
compositeSequenceableLoaderFactory
,
allowChunklessPreparation
);
allowChunklessPreparation
,
useSessionKeys
);
}
@Override
...
...
library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsSampleStreamWrapper.java
View file @
07e3509d
...
...
@@ -21,6 +21,7 @@ import com.google.android.exoplayer2.C;
import
com.google.android.exoplayer2.Format
;
import
com.google.android.exoplayer2.FormatHolder
;
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.ExtractorOutput
;
import
com.google.android.exoplayer2.extractor.SeekMap
;
...
...
@@ -52,6 +53,7 @@ import java.util.ArrayList;
import
java.util.Arrays
;
import
java.util.Collections
;
import
java.util.List
;
import
java.util.Map
;
/**
* Loads {@link HlsMediaChunk}s obtained from a {@link HlsChunkSource}, and provides
...
...
@@ -102,6 +104,7 @@ import java.util.List;
private
final
Runnable
onTracksEndedRunnable
;
private
final
Handler
handler
;
private
final
ArrayList
<
HlsSampleStream
>
hlsSampleStreams
;
private
final
Map
<
String
,
DrmInitData
>
overridingDrmInitData
;
private
SampleQueue
[]
sampleQueues
;
private
int
[]
sampleQueueTrackIds
;
...
...
@@ -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 callback A callback for the wrapper.
* @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 positionUs The position from which to start loading media.
* @param muxedAudioFormat Optional muxed audio {@link Format} as defined by the master playlist.
...
...
@@ -154,6 +161,7 @@ import java.util.List;
int
trackType
,
Callback
callback
,
HlsChunkSource
chunkSource
,
Map
<
String
,
DrmInitData
>
overridingDrmInitData
,
Allocator
allocator
,
long
positionUs
,
Format
muxedAudioFormat
,
...
...
@@ -162,6 +170,7 @@ import java.util.List;
this
.
trackType
=
trackType
;
this
.
callback
=
callback
;
this
.
chunkSource
=
chunkSource
;
this
.
overridingDrmInitData
=
overridingDrmInitData
;
this
.
allocator
=
allocator
;
this
.
muxedAudioFormat
=
muxedAudioFormat
;
this
.
loadErrorHandlingPolicy
=
loadErrorHandlingPolicy
;
...
...
@@ -484,18 +493,28 @@ import java.util.List;
int
result
=
sampleQueues
[
sampleQueueIndex
].
read
(
formatHolder
,
buffer
,
requireFormat
,
loadingFinished
,
lastSeekPositionUs
);
if
(
result
==
C
.
RESULT_FORMAT_READ
&&
sampleQueueIndex
==
primarySampleQueueIndex
)
{
// Fill in primary sample format with information from the track format.
int
chunkUid
=
sampleQueues
[
sampleQueueIndex
].
peekSourceId
();
int
chunkIndex
=
0
;
while
(
chunkIndex
<
mediaChunks
.
size
()
&&
mediaChunks
.
get
(
chunkIndex
).
uid
!=
chunkUid
)
{
chunkIndex
++;
if
(
result
==
C
.
RESULT_FORMAT_READ
)
{
Format
format
=
formatHolder
.
format
;
if
(
sampleQueueIndex
==
primarySampleQueueIndex
)
{
// Fill in primary sample format with information from the track format.
int
chunkUid
=
sampleQueues
[
sampleQueueIndex
].
peekSourceId
();
int
chunkIndex
=
0
;
while
(
chunkIndex
<
mediaChunks
.
size
()
&&
mediaChunks
.
get
(
chunkIndex
).
uid
!=
chunkUid
)
{
chunkIndex
++;
}
Format
trackFormat
=
chunkIndex
<
mediaChunks
.
size
()
?
mediaChunks
.
get
(
chunkIndex
).
trackFormat
:
upstreamTrackFormat
;
format
=
format
.
copyWithManifestFormatInfo
(
trackFormat
);
}
if
(
format
.
drmInitData
!=
null
)
{
DrmInitData
drmInitData
=
overridingDrmInitData
.
get
(
format
.
drmInitData
.
schemeType
);
if
(
drmInitData
!=
null
)
{
format
=
format
.
copyWithDrmInitData
(
drmInitData
);
}
}
Format
trackFormat
=
chunkIndex
<
mediaChunks
.
size
()
?
mediaChunks
.
get
(
chunkIndex
).
trackFormat
:
upstreamTrackFormat
;
formatHolder
.
format
=
formatHolder
.
format
.
copyWithManifestFormatInfo
(
trackFormat
);
formatHolder
.
format
=
format
;
}
return
result
;
}
...
...
library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsMasterPlaylist.java
View file @
07e3509d
...
...
@@ -16,6 +16,7 @@
package
com
.
google
.
android
.
exoplayer2
.
source
.
hls
.
playlist
;
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.util.MimeTypes
;
import
java.util.ArrayList
;
...
...
@@ -37,7 +38,8 @@ public final class HlsMasterPlaylist extends HlsPlaylist {
/* muxedAudioFormat= */
null
,
/* muxedCaptionFormats= */
Collections
.
emptyList
(),
/* 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_AUDIO
=
1
;
...
...
@@ -122,6 +124,8 @@ public final class HlsMasterPlaylist extends HlsPlaylist {
public
final
List
<
Format
>
muxedCaptionFormats
;
/** Contains variable definitions, as defined by the #EXT-X-DEFINE tag. */
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}.
...
...
@@ -133,6 +137,7 @@ public final class HlsMasterPlaylist extends HlsPlaylist {
* @param muxedCaptionFormats See {@link #muxedCaptionFormats}.
* @param hasIndependentSegments See {@link #hasIndependentSegments}.
* @param variableDefinitions See {@link #variableDefinitions}.
* @param sessionKeyDrmInitData See {@link #sessionKeyDrmInitData}.
*/
public
HlsMasterPlaylist
(
String
baseUri
,
...
...
@@ -143,7 +148,8 @@ public final class HlsMasterPlaylist extends HlsPlaylist {
Format
muxedAudioFormat
,
List
<
Format
>
muxedCaptionFormats
,
boolean
hasIndependentSegments
,
Map
<
String
,
String
>
variableDefinitions
)
{
Map
<
String
,
String
>
variableDefinitions
,
List
<
DrmInitData
>
sessionKeyDrmInitData
)
{
super
(
baseUri
,
tags
,
hasIndependentSegments
);
this
.
variants
=
Collections
.
unmodifiableList
(
variants
);
this
.
audios
=
Collections
.
unmodifiableList
(
audios
);
...
...
@@ -152,6 +158,7 @@ public final class HlsMasterPlaylist extends HlsPlaylist {
this
.
muxedCaptionFormats
=
muxedCaptionFormats
!=
null
?
Collections
.
unmodifiableList
(
muxedCaptionFormats
)
:
null
;
this
.
variableDefinitions
=
Collections
.
unmodifiableMap
(
variableDefinitions
);
this
.
sessionKeyDrmInitData
=
Collections
.
unmodifiableList
(
sessionKeyDrmInitData
);
}
@Override
...
...
@@ -165,7 +172,8 @@ public final class HlsMasterPlaylist extends HlsPlaylist {
muxedAudioFormat
,
muxedCaptionFormats
,
hasIndependentSegments
,
variableDefinitions
);
variableDefinitions
,
sessionKeyDrmInitData
);
}
/**
...
...
@@ -186,7 +194,8 @@ public final class HlsMasterPlaylist extends HlsPlaylist {
/* muxedAudioFormat= */
null
,
/* muxedCaptionFormats= */
null
,
/* hasIndependentSegments= */
false
,
/* variableDefinitions= */
Collections
.
emptyMap
());
/* variableDefinitions= */
Collections
.
emptyMap
(),
/* sessionKeyDrmInitData= */
Collections
.
emptyList
());
}
private
static
List
<
HlsUrl
>
copyRenditionsList
(
...
...
library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistParser.java
View file @
07e3509d
...
...
@@ -34,7 +34,6 @@ import java.io.BufferedReader;
import
java.io.IOException
;
import
java.io.InputStream
;
import
java.io.InputStreamReader
;
import
java.io.UnsupportedEncodingException
;
import
java.util.ArrayDeque
;
import
java.util.ArrayList
;
import
java.util.Collections
;
...
...
@@ -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_ENDLIST
=
"#EXT-X-ENDLIST"
;
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_GAP
=
"#EXT-X-GAP"
;
...
...
@@ -253,6 +253,7 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
ArrayList
<
HlsMasterPlaylist
.
HlsUrl
>
audios
=
new
ArrayList
<>();
ArrayList
<
HlsMasterPlaylist
.
HlsUrl
>
subtitles
=
new
ArrayList
<>();
ArrayList
<
String
>
mediaTags
=
new
ArrayList
<>();
ArrayList
<
DrmInitData
>
sessionKeyDrmInitData
=
new
ArrayList
<>();
ArrayList
<
String
>
tags
=
new
ArrayList
<>();
Format
muxedAudioFormat
=
null
;
List
<
Format
>
muxedCaptionFormats
=
null
;
...
...
@@ -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
// tags.
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
))
{
noClosedCaptions
|=
line
.
contains
(
ATTR_CLOSED_CAPTIONS_NONE
);
int
bitrate
=
parseIntAttr
(
line
,
REGEX_BANDWIDTH
);
...
...
@@ -423,6 +433,7 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
if
(
noClosedCaptions
)
{
muxedCaptionFormats
=
Collections
.
emptyList
();
}
return
new
HlsMasterPlaylist
(
baseUri
,
tags
,
...
...
@@ -432,7 +443,8 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
muxedAudioFormat
,
muxedCaptionFormats
,
hasIndependentSegmentsTag
,
variableDefinitions
);
variableDefinitions
,
sessionKeyDrmInitData
);
}
private
static
HlsMediaPlaylist
parseMediaPlaylist
(
...
...
@@ -557,17 +569,9 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
}
}
else
{
if
(
encryptionScheme
==
null
)
{
encryptionScheme
=
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
);
encryptionScheme
=
parseEncryptionScheme
(
method
);
}
SchemeData
schemeData
=
parseDrmSchemeData
(
line
,
keyFormat
,
variableDefinitions
);
if
(
schemeData
!=
null
)
{
cachedDrmInitData
=
null
;
currentSchemeDatas
.
put
(
keyFormat
,
schemeData
);
...
...
@@ -713,40 +717,35 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
:
Format
.
NO_VALUE
;
}
private
static
@Nullable
SchemeData
parsePlayReadySchemeData
(
String
line
,
Map
<
String
,
String
>
variableDefinitions
)
throws
ParserException
{
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
(
@Nullable
private
static
SchemeData
parseDrmSchemeData
(
String
line
,
String
keyFormat
,
Map
<
String
,
String
>
variableDefinitions
)
throws
ParserException
{
String
keyFormatVersions
=
parseOptionalStringAttr
(
line
,
REGEX_KEYFORMATVERSIONS
,
"1"
,
variableDefinitions
);
if
(
KEYFORMAT_WIDEVINE_PSSH_BINARY
.
equals
(
keyFormat
))
{
String
uriString
=
parseStringAttr
(
line
,
REGEX_URI
,
variableDefinitions
);
return
new
SchemeData
(
C
.
WIDEVINE_UUID
,
MimeTypes
.
VIDEO_MP4
,
Base64
.
decode
(
uriString
.
substring
(
uriString
.
indexOf
(
','
)),
Base64
.
DEFAULT
));
}
if
(
KEYFORMAT_WIDEVINE_PSSH_JSON
.
equals
(
keyFormat
))
{
try
{
return
new
SchemeData
(
C
.
WIDEVINE_UUID
,
"hls"
,
line
.
getBytes
(
C
.
UTF8_NAME
)
);
}
catch
(
UnsupportedEncodingException
e
)
{
throw
new
ParserException
(
e
);
}
}
else
if
(
KEYFORMAT_WIDEVINE_PSSH_JSON
.
equals
(
keyFormat
))
{
return
new
SchemeData
(
C
.
WIDEVINE_UUID
,
"hls"
,
Util
.
getUtf8Bytes
(
line
));
}
else
if
(
KEYFORMAT_PLAYREADY
.
equals
(
keyFormat
)
&&
"1"
.
equals
(
keyFormatVersions
))
{
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
);
}
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
{
return
Integer
.
parseInt
(
parseStringAttr
(
line
,
pattern
,
Collections
.
emptyMap
()));
}
...
...
library/hls/src/test/java/com/google/android/exoplayer2/source/hls/HlsMediaPeriodTest.java
View file @
07e3509d
...
...
@@ -88,7 +88,8 @@ public final class HlsMediaPeriodTest {
/* mediaTimeOffsetMs= */
0
),
mock
(
Allocator
.
class
),
mock
(
CompositeSequenceableLoaderFactory
.
class
),
/* allowChunklessPreparation =*/
true
);
/* allowChunklessPreparation =*/
true
,
/* useSessionKeys= */
false
);
};
MediaPeriodAsserts
.
assertGetStreamKeysAndManifestFilterIntegration
(
...
...
@@ -110,7 +111,8 @@ public final class HlsMediaPeriodTest {
muxedAudioFormat
,
muxedCaptionFormats
,
/* hasIndependentSegments= */
true
,
/* variableDefinitions= */
Collections
.
emptyMap
());
/* variableDefinitions= */
Collections
.
emptyMap
(),
/* sessionKeyDrmInitData= */
Collections
.
emptyList
());
}
private
static
HlsUrl
createMuxedVideoAudioVariantHlsUrl
(
int
bitrate
)
{
...
...
library/hls/src/test/java/com/google/android/exoplayer2/source/hls/playlist/HlsMediaPlaylistParserTest.java
View file @
07e3509d
...
...
@@ -456,7 +456,8 @@ public class HlsMediaPlaylistParserTest {
/* muxedAudioFormat= */
null
,
/* muxedCaptionFormats= */
null
,
/* hasIndependentSegments= */
true
,
/* variableDefinitions */
Collections
.
emptyMap
());
/* variableDefinitions= */
Collections
.
emptyMap
(),
/* sessionKeyDrmInitData= */
Collections
.
emptyList
());
HlsMediaPlaylist
playlistWithInheritance
=
(
HlsMediaPlaylist
)
new
HlsPlaylistParser
(
masterPlaylist
).
parse
(
playlistUri
,
inputStream
);
assertThat
(
playlistWithInheritance
.
hasIndependentSegments
).
isTrue
();
...
...
@@ -515,7 +516,8 @@ public class HlsMediaPlaylistParserTest {
/* muxedAudioFormat= */
null
,
/* muxedCaptionFormats= */
Collections
.
emptyList
(),
/* hasIndependentSegments= */
false
,
variableDefinitions
);
variableDefinitions
,
/* sessionKeyDrmInitData= */
Collections
.
emptyList
());
HlsMediaPlaylist
playlist
=
(
HlsMediaPlaylist
)
new
HlsPlaylistParser
(
masterPlaylist
).
parse
(
playlistUri
,
inputStream
);
for
(
int
i
=
1
;
i
<=
4
;
i
++)
{
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment