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
e52a044e
authored
Oct 21, 2020
by
christosts
Committed by
Oliver Woodman
Oct 21, 2020
Browse files
Options
_('Browse Files')
Download
Email Patches
Plain Diff
Parse #EXT-X-SERVER-CONTROL and #EXT-X-PART-INF in HLS media playlists.
PiperOrigin-RevId: 338232910
parent
1051580a
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
211 additions
and
13 deletions
library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsMediaPlaylist.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/playlist/HlsMediaPlaylistParserTest.java
library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsMediaPlaylist.java
View file @
e52a044e
...
...
@@ -29,6 +29,54 @@ import java.util.List;
/** Represents an HLS media playlist. */
public
final
class
HlsMediaPlaylist
extends
HlsPlaylist
{
/** Server control attributes. */
public
static
final
class
ServerControl
{
/**
* The skip boundary for delta updates in microseconds, or {@link C#TIME_UNSET} if delta updates
* are not supported.
*/
public
final
long
skipUntilUs
;
/**
* Whether the playlist can produce delta updates that skip older #EXT-X-DATERANGE tags in
* addition to media segments.
*/
public
final
boolean
canSkipDateRanges
;
/**
* The server-recommended live offset in microseconds, or {@link C#TIME_UNSET} if none defined.
*/
public
final
long
holdBackUs
;
/**
* The server-recommended live offset in microseconds in low-latency mode, or {@link
* C#TIME_UNSET} if none defined.
*/
public
final
long
partHoldBackUs
;
/** Whether the server supports blocking playlist reload. */
public
final
boolean
canBlockReload
;
/**
* Creates a new instance.
*
* @param skipUntilUs See {@link #skipUntilUs}.
* @param canSkipDateRanges See {@link #canSkipDateRanges}.
* @param holdBackUs See {@link #holdBackUs}.
* @param partHoldBackUs See {@link #partHoldBackUs}.
* @param canBlockReload See {@link #canBlockReload}.
*/
public
ServerControl
(
long
skipUntilUs
,
boolean
canSkipDateRanges
,
long
holdBackUs
,
long
partHoldBackUs
,
boolean
canBlockReload
)
{
this
.
skipUntilUs
=
skipUntilUs
;
this
.
canSkipDateRanges
=
canSkipDateRanges
;
this
.
holdBackUs
=
holdBackUs
;
this
.
partHoldBackUs
=
partHoldBackUs
;
this
.
canBlockReload
=
canBlockReload
;
}
}
/** Media segment reference. */
@SuppressWarnings
(
"ComparableType"
)
public
static
final
class
Segment
implements
Comparable
<
Long
>
{
...
...
@@ -208,8 +256,11 @@ public final class HlsMediaPlaylist extends HlsPlaylist {
*/
public
final
long
targetDurationUs
;
/**
* Whether the playlist contains the #EXT-X-ENDLIST tag.
* The target duration for segment parts, as defined by #EXT-X-PART-INF, or {@link C#TIME_UNSET}
* if undefined.
*/
public
final
long
partTargetDurationUs
;
/** Whether the playlist contains the #EXT-X-ENDLIST tag. */
public
final
boolean
hasEndTag
;
/**
* Whether the playlist contains a #EXT-X-PROGRAM-DATE-TIME tag.
...
...
@@ -228,6 +279,8 @@ public final class HlsMediaPlaylist extends HlsPlaylist {
* The total duration of the playlist in microseconds.
*/
public
final
long
durationUs
;
/** The attributes of the #EXT-X-SERVER-CONTROL header. */
public
final
ServerControl
serverControl
;
/**
* @param playlistType See {@link #playlistType}.
...
...
@@ -245,6 +298,7 @@ public final class HlsMediaPlaylist extends HlsPlaylist {
* @param protectionSchemes See {@link #protectionSchemes}.
* @param hasProgramDateTime See {@link #hasProgramDateTime}.
* @param segments See {@link #segments}.
* @param serverControl See {@link #serverControl}
*/
public
HlsMediaPlaylist
(
@PlaylistType
int
playlistType
,
...
...
@@ -257,11 +311,13 @@ public final class HlsMediaPlaylist extends HlsPlaylist {
long
mediaSequence
,
int
version
,
long
targetDurationUs
,
long
partTargetDurationUs
,
boolean
hasIndependentSegments
,
boolean
hasEndTag
,
boolean
hasProgramDateTime
,
@Nullable
DrmInitData
protectionSchemes
,
List
<
Segment
>
segments
)
{
List
<
Segment
>
segments
,
ServerControl
serverControl
)
{
super
(
baseUri
,
tags
,
hasIndependentSegments
);
this
.
playlistType
=
playlistType
;
this
.
startTimeUs
=
startTimeUs
;
...
...
@@ -270,6 +326,7 @@ public final class HlsMediaPlaylist extends HlsPlaylist {
this
.
mediaSequence
=
mediaSequence
;
this
.
version
=
version
;
this
.
targetDurationUs
=
targetDurationUs
;
this
.
partTargetDurationUs
=
partTargetDurationUs
;
this
.
hasEndTag
=
hasEndTag
;
this
.
hasProgramDateTime
=
hasProgramDateTime
;
this
.
protectionSchemes
=
protectionSchemes
;
...
...
@@ -282,6 +339,7 @@ public final class HlsMediaPlaylist extends HlsPlaylist {
}
this
.
startOffsetUs
=
startOffsetUs
==
C
.
TIME_UNSET
?
C
.
TIME_UNSET
:
startOffsetUs
>=
0
?
startOffsetUs
:
durationUs
+
startOffsetUs
;
this
.
serverControl
=
serverControl
;
}
@Override
...
...
@@ -337,11 +395,13 @@ public final class HlsMediaPlaylist extends HlsPlaylist {
mediaSequence
,
version
,
targetDurationUs
,
partTargetDurationUs
,
hasIndependentSegments
,
hasEndTag
,
hasProgramDateTime
,
protectionSchemes
,
segments
);
segments
,
serverControl
);
}
/**
...
...
@@ -363,11 +423,13 @@ public final class HlsMediaPlaylist extends HlsPlaylist {
mediaSequence
,
version
,
targetDurationUs
,
partTargetDurationUs
,
hasIndependentSegments
,
/* hasEndTag= */
true
,
hasProgramDateTime
,
protectionSchemes
,
segments
);
segments
,
serverControl
);
}
}
library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistParser.java
View file @
e52a044e
...
...
@@ -15,6 +15,8 @@
*/
package
com
.
google
.
android
.
exoplayer2
.
source
.
hls
.
playlist
;
import
static
com
.
google
.
android
.
exoplayer2
.
util
.
Assertions
.
checkNotNull
;
import
android.net.Uri
;
import
android.text.TextUtils
;
import
android.util.Base64
;
...
...
@@ -68,7 +70,9 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
private
static
final
String
TAG_VERSION
=
"#EXT-X-VERSION"
;
private
static
final
String
TAG_PLAYLIST_TYPE
=
"#EXT-X-PLAYLIST-TYPE"
;
private
static
final
String
TAG_DEFINE
=
"#EXT-X-DEFINE"
;
private
static
final
String
TAG_SERVER_CONTROL
=
"#EXT-X-SERVER-CONTROL"
;
private
static
final
String
TAG_STREAM_INF
=
"#EXT-X-STREAM-INF"
;
private
static
final
String
TAG_PART_INF
=
"#EXT-X-PART-INF"
;
private
static
final
String
TAG_I_FRAME_STREAM_INF
=
"#EXT-X-I-FRAME-STREAM-INF"
;
private
static
final
String
TAG_IFRAME
=
"#EXT-X-I-FRAMES-ONLY"
;
private
static
final
String
TAG_MEDIA
=
"#EXT-X-MEDIA"
;
...
...
@@ -122,9 +126,20 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
private
static
final
Pattern
REGEX_FRAME_RATE
=
Pattern
.
compile
(
"FRAME-RATE=([\\d\\.]+)\\b"
);
private
static
final
Pattern
REGEX_TARGET_DURATION
=
Pattern
.
compile
(
TAG_TARGET_DURATION
+
":(\\d+)\\b"
);
private
static
final
Pattern
REGEX_PART_TARGET_DURATION
=
Pattern
.
compile
(
"PART-TARGET=([\\d\\.]+)\\b"
);
private
static
final
Pattern
REGEX_VERSION
=
Pattern
.
compile
(
TAG_VERSION
+
":(\\d+)\\b"
);
private
static
final
Pattern
REGEX_PLAYLIST_TYPE
=
Pattern
.
compile
(
TAG_PLAYLIST_TYPE
+
":(.+)\\b"
);
private
static
final
Pattern
REGEX_CAN_SKIP_UNTIL
=
Pattern
.
compile
(
"CAN-SKIP-UNTIL=([\\d\\.]+)\\b"
);
private
static
final
Pattern
REGEX_CAN_SKIP_DATE_RANGES
=
compileBooleanAttrPattern
(
"CAN-SKIP-DATERANGES"
);
private
static
final
Pattern
REGEX_HOLD_BACK
=
Pattern
.
compile
(
"[:|,]HOLD-BACK=([\\d\\.]+)\\b"
);
private
static
final
Pattern
REGEX_PART_HOLD_BACK
=
Pattern
.
compile
(
"PART-HOLD-BACK=([\\d\\.]+)\\b"
);
private
static
final
Pattern
REGEX_CAN_BLOCK_RELOAD
=
compileBooleanAttrPattern
(
"CAN-BLOCK-RELOAD"
);
private
static
final
Pattern
REGEX_MEDIA_SEQUENCE
=
Pattern
.
compile
(
TAG_MEDIA_SEQUENCE
+
":(\\d+)\\b"
);
private
static
final
Pattern
REGEX_MEDIA_DURATION
=
Pattern
.
compile
(
TAG_MEDIA_DURATION
...
...
@@ -394,7 +409,7 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
new
HlsTrackMetadataEntry
(
/* groupId= */
null
,
/* name= */
null
,
Assertions
.
checkNotNull
(
urlToVariantInfos
.
get
(
variant
.
url
)));
checkNotNull
(
urlToVariantInfos
.
get
(
variant
.
url
)));
Metadata
metadata
=
new
Metadata
(
hlsMetadataEntry
);
Format
format
=
variant
.
format
.
buildUpon
().
setMetadata
(
metadata
).
build
();
deduplicatedVariants
.
add
(
variant
.
copyWithFormat
(
format
));
...
...
@@ -566,6 +581,7 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
long
mediaSequence
=
0
;
int
version
=
1
;
// Default version == 1.
long
targetDurationUs
=
C
.
TIME_UNSET
;
long
partTargetDurationUs
=
C
.
TIME_UNSET
;
boolean
hasIndependentSegmentsTag
=
masterPlaylist
.
hasIndependentSegments
;
boolean
hasEndTag
=
false
;
@Nullable
Segment
initializationSegment
=
null
;
...
...
@@ -586,6 +602,13 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
boolean
isIFrameOnly
=
false
;
long
segmentMediaSequence
=
0
;
boolean
hasGapTag
=
false
;
HlsMediaPlaylist
.
ServerControl
serverControl
=
new
HlsMediaPlaylist
.
ServerControl
(
/* skipUntilUs= */
C
.
TIME_UNSET
,
/* canSkipDateRanges= */
false
,
/* holdBackUs= */
C
.
TIME_UNSET
,
/* partHoldBackUs= */
C
.
TIME_UNSET
,
/* canBlockReload= */
false
);
DrmInitData
playlistProtectionSchemes
=
null
;
String
fullSegmentEncryptionKeyUri
=
null
;
...
...
@@ -614,6 +637,11 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
isIFrameOnly
=
true
;
}
else
if
(
line
.
startsWith
(
TAG_START
))
{
startOffsetUs
=
(
long
)
(
parseDoubleAttr
(
line
,
REGEX_TIME_OFFSET
)
*
C
.
MICROS_PER_SECOND
);
}
else
if
(
line
.
startsWith
(
TAG_SERVER_CONTROL
))
{
serverControl
=
parseServerControl
(
line
);
}
else
if
(
line
.
startsWith
(
TAG_PART_INF
))
{
double
partTargetDurationSeconds
=
parseDoubleAttr
(
line
,
REGEX_PART_TARGET_DURATION
);
partTargetDurationUs
=
(
long
)
(
partTargetDurationSeconds
*
C
.
MICROS_PER_SECOND
);
}
else
if
(
line
.
startsWith
(
TAG_INIT_SEGMENT
))
{
String
uri
=
parseStringAttr
(
line
,
REGEX_URI
,
variableDefinitions
);
String
byteRange
=
parseOptionalStringAttr
(
line
,
REGEX_ATTR_BYTERANGE
,
variableDefinitions
);
...
...
@@ -786,6 +814,7 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
hasGapTag
=
false
;
}
}
return
new
HlsMediaPlaylist
(
playlistType
,
baseUri
,
...
...
@@ -797,11 +826,13 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
mediaSequence
,
version
,
targetDurationUs
,
partTargetDurationUs
,
hasIndependentSegmentsTag
,
hasEndTag
,
/* hasProgramDateTime= */
playlistStartTimeUs
!=
0
,
playlistProtectionSchemes
,
segments
);
segments
,
serverControl
);
}
@C
.
SelectionFlags
...
...
@@ -866,6 +897,33 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
return
null
;
}
private
static
HlsMediaPlaylist
.
ServerControl
parseServerControl
(
String
line
)
{
double
skipUntilSeconds
=
parseOptionalDoubleAttr
(
line
,
REGEX_CAN_SKIP_UNTIL
,
/* defaultValue= */
C
.
TIME_UNSET
);
long
skipUntilUs
=
skipUntilSeconds
==
C
.
TIME_UNSET
?
C
.
TIME_UNSET
:
(
long
)
(
skipUntilSeconds
*
C
.
MICROS_PER_SECOND
);
boolean
canSkipDateRanges
=
parseOptionalBooleanAttribute
(
line
,
REGEX_CAN_SKIP_DATE_RANGES
,
/* defaultValue= */
false
);
double
holdBackSeconds
=
parseOptionalDoubleAttr
(
line
,
REGEX_HOLD_BACK
,
/* defaultValue= */
C
.
TIME_UNSET
);
long
holdBackUs
=
holdBackSeconds
==
C
.
TIME_UNSET
?
C
.
TIME_UNSET
:
(
long
)
(
holdBackSeconds
*
C
.
MICROS_PER_SECOND
);
double
partHoldBackSeconds
=
parseOptionalDoubleAttr
(
line
,
REGEX_PART_HOLD_BACK
,
C
.
TIME_UNSET
);
long
partHoldBackUs
=
partHoldBackSeconds
==
C
.
TIME_UNSET
?
C
.
TIME_UNSET
:
(
long
)
(
partHoldBackSeconds
*
C
.
MICROS_PER_SECOND
);
boolean
canBlockReload
=
parseOptionalBooleanAttribute
(
line
,
REGEX_CAN_BLOCK_RELOAD
,
/* defaultValue= */
false
);
return
new
HlsMediaPlaylist
.
ServerControl
(
skipUntilUs
,
canSkipDateRanges
,
holdBackUs
,
partHoldBackUs
,
canBlockReload
);
}
private
static
String
parseEncryptionScheme
(
String
method
)
{
return
METHOD_SAMPLE_AES_CENC
.
equals
(
method
)
||
METHOD_SAMPLE_AES_CTR
.
equals
(
method
)
?
C
.
CENC_TYPE_cenc
...
...
@@ -879,7 +937,7 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
private
static
int
parseOptionalIntAttr
(
String
line
,
Pattern
pattern
,
int
defaultValue
)
{
Matcher
matcher
=
pattern
.
matcher
(
line
);
if
(
matcher
.
find
())
{
return
Integer
.
parseInt
(
Assertions
.
checkNotNull
(
matcher
.
group
(
1
)));
return
Integer
.
parseInt
(
checkNotNull
(
matcher
.
group
(
1
)));
}
return
defaultValue
;
}
...
...
@@ -914,13 +972,20 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
@PolyNull
String
defaultValue
,
Map
<
String
,
String
>
variableDefinitions
)
{
Matcher
matcher
=
pattern
.
matcher
(
line
);
@PolyNull
String
value
=
matcher
.
find
()
?
Assertions
.
checkNotNull
(
matcher
.
group
(
1
))
:
defaultValue
;
@PolyNull
String
value
=
matcher
.
find
()
?
checkNotNull
(
matcher
.
group
(
1
))
:
defaultValue
;
return
variableDefinitions
.
isEmpty
()
||
value
==
null
?
value
:
replaceVariableReferences
(
value
,
variableDefinitions
);
}
private
static
double
parseOptionalDoubleAttr
(
String
line
,
Pattern
pattern
,
double
defaultValue
)
{
Matcher
matcher
=
pattern
.
matcher
(
line
);
if
(
matcher
.
find
())
{
return
Double
.
parseDouble
(
checkNotNull
(
matcher
.
group
(
1
)));
}
return
defaultValue
;
}
private
static
String
replaceVariableReferences
(
String
string
,
Map
<
String
,
String
>
variableDefinitions
)
{
Matcher
matcher
=
REGEX_VARIABLE_REFERENCE
.
matcher
(
string
);
...
...
@@ -970,7 +1035,7 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
return
true
;
}
if
(!
extraLines
.
isEmpty
())
{
next
=
Assertions
.
checkNotNull
(
extraLines
.
poll
());
next
=
checkNotNull
(
extraLines
.
poll
());
return
true
;
}
while
((
next
=
reader
.
readLine
())
!=
null
)
{
...
...
@@ -992,7 +1057,5 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
throw
new
NoSuchElementException
();
}
}
}
}
library/hls/src/test/java/com/google/android/exoplayer2/source/hls/playlist/HlsMediaPlaylistParserTest.java
View file @
e52a044e
...
...
@@ -45,7 +45,7 @@ public class HlsMediaPlaylistParserTest {
"#EXTM3U\n"
+
"#EXT-X-VERSION:3\n"
+
"#EXT-X-PLAYLIST-TYPE:VOD\n"
+
"#EXT-X-START:TIME-OFFSET=-25"
+
"#EXT-X-START:TIME-OFFSET=-25
\n
"
+
"#EXT-X-TARGETDURATION:8\n"
+
"#EXT-X-MEDIA-SEQUENCE:2679\n"
+
"#EXT-X-DISCONTINUITY-SEQUENCE:4\n"
...
...
@@ -86,6 +86,8 @@ public class HlsMediaPlaylistParserTest {
assertThat
(
mediaPlaylist
.
version
).
isEqualTo
(
3
);
assertThat
(
mediaPlaylist
.
hasEndTag
).
isTrue
();
assertThat
(
mediaPlaylist
.
protectionSchemes
).
isNull
();
assertThat
(
mediaPlaylist
.
targetDurationUs
).
isEqualTo
(
8000000
);
assertThat
(
mediaPlaylist
.
partTargetDurationUs
).
isEqualTo
(
C
.
TIME_UNSET
);
List
<
Segment
>
segments
=
mediaPlaylist
.
segments
;
assertThat
(
segments
).
isNotNull
();
assertThat
(
segments
).
hasSize
(
5
);
...
...
@@ -219,6 +221,7 @@ public class HlsMediaPlaylistParserTest {
+
"https://priv.example.com/2.ts\n"
+
"#EXT-X-ENDLIST\n"
;
InputStream
inputStream
=
new
ByteArrayInputStream
(
Util
.
getUtf8Bytes
(
playlistString
));
HlsMediaPlaylist
playlist
=
(
HlsMediaPlaylist
)
new
HlsPlaylistParser
().
parse
(
playlistUri
,
inputStream
);
assertThat
(
playlist
.
protectionSchemes
.
schemeType
).
isEqualTo
(
C
.
CENC_TYPE_cenc
);
...
...
@@ -227,6 +230,76 @@ public class HlsMediaPlaylistParserTest {
}
@Test
public
void
parseMediaPlaylist_withPartMediaInformation_succeeds
()
throws
IOException
{
Uri
playlistUri
=
Uri
.
parse
(
"https://example.com/test.m3u8"
);
String
playlistString
=
"#EXTM3U\n"
+
"#EXT-X-TARGETDURATION:4\n"
+
"#EXT-X-VERSION:6\n"
+
"#EXT-X-SERVER-CONTROL:PART-HOLD-BACK=1.234\n"
+
"#EXT-X-PART-INF:PART-TARGET=0.5\n"
+
"#EXT-X-MEDIA-SEQUENCE:266\n"
+
"#EXT-X-PROGRAM-DATE-TIME:2019-02-14T02:13:36.106Z\n"
+
"#EXT-X-MAP:URI=\"init.mp4\"\n"
+
"#EXTINF:4.00008,\n"
+
"fileSequence266.mp4"
;
InputStream
inputStream
=
new
ByteArrayInputStream
(
Util
.
getUtf8Bytes
(
playlistString
));
HlsMediaPlaylist
playlist
=
(
HlsMediaPlaylist
)
new
HlsPlaylistParser
().
parse
(
playlistUri
,
inputStream
);
assertThat
(
playlist
.
partTargetDurationUs
).
isEqualTo
(
500000
);
}
@Test
public
void
parseMediaPlaylist_withoutServerControl_serverControlDefaultValues
()
throws
IOException
{
Uri
playlistUri
=
Uri
.
parse
(
"https://example.com/test.m3u8"
);
String
playlistString
=
"#EXTM3U\n"
+
"#EXT-X-MEDIA-SEQUENCE:0\n"
+
"#EXTINF:8,\n"
+
"https://priv.example.com/1.ts\n"
+
"#EXT-X-ENDLIST\n"
;
InputStream
inputStream
=
new
ByteArrayInputStream
(
Util
.
getUtf8Bytes
(
playlistString
));
HlsMediaPlaylist
playlist
=
(
HlsMediaPlaylist
)
new
HlsPlaylistParser
().
parse
(
playlistUri
,
inputStream
);
assertThat
(
playlist
.
serverControl
.
canBlockReload
).
isFalse
();
assertThat
(
playlist
.
serverControl
.
partHoldBackUs
).
isEqualTo
(
C
.
TIME_UNSET
);
assertThat
(
playlist
.
serverControl
.
holdBackUs
).
isEqualTo
(
C
.
TIME_UNSET
);
assertThat
(
playlist
.
serverControl
.
skipUntilUs
).
isEqualTo
(
C
.
TIME_UNSET
);
assertThat
(
playlist
.
serverControl
.
canSkipDateRanges
).
isFalse
();
}
@Test
public
void
parseMediaPlaylist_withServerControl_succeeds
()
throws
IOException
{
Uri
playlistUri
=
Uri
.
parse
(
"https://example.com/test.m3u8"
);
String
playlistString
=
"#EXTM3U\n"
+
"#EXT-X-TARGETDURATION:4\n"
+
"#EXT-X-VERSION:6\n"
+
"#EXT-X-SERVER-CONTROL:CAN-BLOCK-RELOAD=YES,HOLD-BACK=18.5,PART-HOLD-BACK=1.234,"
+
"CAN-SKIP-UNTIL=24.0,CAN-SKIP-DATERANGES=YES\n"
+
"#EXT-X-PART-INF:PART-TARGET=0.5\n"
+
"#EXT-X-MEDIA-SEQUENCE:266\n"
+
"#EXT-X-PROGRAM-DATE-TIME:2019-02-14T02:13:36.106Z\n"
+
"#EXT-X-MAP:URI=\"init.mp4\"\n"
+
"#EXTINF:4.00008,\n"
+
"fileSequence266.mp4"
;
InputStream
inputStream
=
new
ByteArrayInputStream
(
Util
.
getUtf8Bytes
(
playlistString
));
HlsMediaPlaylist
playlist
=
(
HlsMediaPlaylist
)
new
HlsPlaylistParser
().
parse
(
playlistUri
,
inputStream
);
assertThat
(
playlist
.
serverControl
.
canBlockReload
).
isTrue
();
assertThat
(
playlist
.
serverControl
.
partHoldBackUs
).
isEqualTo
(
1234000
);
assertThat
(
playlist
.
serverControl
.
holdBackUs
).
isEqualTo
(
18500000
);
assertThat
(
playlist
.
serverControl
.
skipUntilUs
).
isEqualTo
(
24000000
);
assertThat
(
playlist
.
serverControl
.
canSkipDateRanges
).
isTrue
();
}
@Test
public
void
multipleExtXKeysForSingleSegment
()
throws
Exception
{
Uri
playlistUri
=
Uri
.
parse
(
"https://example.com/test.m3u8"
);
String
playlistString
=
...
...
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