Add support for HLS's #EXT-X-PLAYLIST-TYPE

Issue:#805

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=145991145
This commit is contained in:
aquilescanta 2017-01-30 07:47:31 -08:00 committed by Oliver Woodman
parent 7ee8567f4a
commit e6bbd397d5
4 changed files with 45 additions and 13 deletions

View File

@ -35,6 +35,7 @@ public class HlsMediaPlaylistParserTest extends TestCase {
Uri playlistUri = Uri.parse("https://example.com/test.m3u8"); Uri playlistUri = Uri.parse("https://example.com/test.m3u8");
String playlistString = "#EXTM3U\n" String playlistString = "#EXTM3U\n"
+ "#EXT-X-VERSION:3\n" + "#EXT-X-VERSION:3\n"
+ "#EXT-X-PLAYLIST-TYPE:VOD\n"
+ "#EXT-X-TARGETDURATION:8\n" + "#EXT-X-TARGETDURATION:8\n"
+ "#EXT-X-MEDIA-SEQUENCE:2679\n" + "#EXT-X-MEDIA-SEQUENCE:2679\n"
+ "#EXT-X-DISCONTINUITY-SEQUENCE:4\n" + "#EXT-X-DISCONTINUITY-SEQUENCE:4\n"
@ -71,6 +72,7 @@ public class HlsMediaPlaylistParserTest extends TestCase {
assertEquals(HlsPlaylist.TYPE_MEDIA, playlist.type); assertEquals(HlsPlaylist.TYPE_MEDIA, playlist.type);
HlsMediaPlaylist mediaPlaylist = (HlsMediaPlaylist) playlist; HlsMediaPlaylist mediaPlaylist = (HlsMediaPlaylist) playlist;
assertEquals(HlsMediaPlaylist.PLAYLIST_TYPE_VOD, mediaPlaylist.playlistType);
assertEquals(2679, mediaPlaylist.mediaSequence); assertEquals(2679, mediaPlaylist.mediaSequence);
assertEquals(3, mediaPlaylist.version); assertEquals(3, mediaPlaylist.version);

View File

@ -15,7 +15,10 @@
*/ */
package com.google.android.exoplayer2.source.hls.playlist; package com.google.android.exoplayer2.source.hls.playlist;
import android.support.annotation.IntDef;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
@ -65,6 +68,18 @@ public final class HlsMediaPlaylist extends HlsPlaylist {
} }
/**
* Type of the playlist as specified by #EXT-X-PLAYLIST-TYPE.
*/
@Retention(RetentionPolicy.SOURCE)
@IntDef({PLAYLIST_TYPE_UNKNOWN, PLAYLIST_TYPE_VOD, PLAYLIST_TYPE_EVENT})
public @interface PlaylistType {}
public static final int PLAYLIST_TYPE_UNKNOWN = 0;
public static final int PLAYLIST_TYPE_VOD = 1;
public static final int PLAYLIST_TYPE_EVENT = 2;
@PlaylistType
public final int playlistType;
public final long startOffsetUs; public final long startOffsetUs;
public final long startTimeUs; public final long startTimeUs;
public final boolean hasDiscontinuitySequence; public final boolean hasDiscontinuitySequence;
@ -78,11 +93,12 @@ public final class HlsMediaPlaylist extends HlsPlaylist {
public final List<Segment> segments; public final List<Segment> segments;
public final long durationUs; public final long durationUs;
public HlsMediaPlaylist(String baseUri, long startOffsetUs, long startTimeUs, public HlsMediaPlaylist(@PlaylistType int playlistType, String baseUri, long startOffsetUs,
boolean hasDiscontinuitySequence, int discontinuitySequence, int mediaSequence, int version, long startTimeUs, boolean hasDiscontinuitySequence, int discontinuitySequence,
long targetDurationUs, boolean hasEndTag, boolean hasProgramDateTime, int mediaSequence, int version, long targetDurationUs, boolean hasEndTag,
Segment initializationSegment, List<Segment> segments) { boolean hasProgramDateTime, Segment initializationSegment, List<Segment> segments) {
super(baseUri, HlsPlaylist.TYPE_MEDIA); super(baseUri, HlsPlaylist.TYPE_MEDIA);
this.playlistType = playlistType;
this.startTimeUs = startTimeUs; this.startTimeUs = startTimeUs;
this.hasDiscontinuitySequence = hasDiscontinuitySequence; this.hasDiscontinuitySequence = hasDiscontinuitySequence;
this.discontinuitySequence = discontinuitySequence; this.discontinuitySequence = discontinuitySequence;
@ -137,9 +153,9 @@ public final class HlsMediaPlaylist extends HlsPlaylist {
* @return The playlist. * @return The playlist.
*/ */
public HlsMediaPlaylist copyWith(long startTimeUs, int discontinuitySequence) { public HlsMediaPlaylist copyWith(long startTimeUs, int discontinuitySequence) {
return new HlsMediaPlaylist(baseUri, startOffsetUs, startTimeUs, true, discontinuitySequence, return new HlsMediaPlaylist(playlistType, baseUri, startOffsetUs, startTimeUs, true,
mediaSequence, version, targetDurationUs, hasEndTag, hasProgramDateTime, discontinuitySequence, mediaSequence, version, targetDurationUs, hasEndTag,
initializationSegment, segments); hasProgramDateTime, initializationSegment, segments);
} }
/** /**
@ -152,9 +168,9 @@ public final class HlsMediaPlaylist extends HlsPlaylist {
if (this.hasEndTag) { if (this.hasEndTag) {
return this; return this;
} }
return new HlsMediaPlaylist(baseUri, startOffsetUs, startTimeUs, hasDiscontinuitySequence, return new HlsMediaPlaylist(playlistType, baseUri, startOffsetUs, startTimeUs,
discontinuitySequence, mediaSequence, version, targetDurationUs, true, hasProgramDateTime, hasDiscontinuitySequence, discontinuitySequence, mediaSequence, version, targetDurationUs,
initializationSegment, segments); true, hasProgramDateTime, initializationSegment, segments);
} }
} }

View File

@ -43,6 +43,7 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
private static final String PLAYLIST_HEADER = "#EXTM3U"; private static final String PLAYLIST_HEADER = "#EXTM3U";
private static final String TAG_VERSION = "#EXT-X-VERSION"; 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_STREAM_INF = "#EXT-X-STREAM-INF"; private static final String TAG_STREAM_INF = "#EXT-X-STREAM-INF";
private static final String TAG_MEDIA = "#EXT-X-MEDIA"; private static final String TAG_MEDIA = "#EXT-X-MEDIA";
private static final String TAG_TARGET_DURATION = "#EXT-X-TARGETDURATION"; private static final String TAG_TARGET_DURATION = "#EXT-X-TARGETDURATION";
@ -74,6 +75,8 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
private static final Pattern REGEX_TARGET_DURATION = Pattern.compile(TAG_TARGET_DURATION private static final Pattern REGEX_TARGET_DURATION = Pattern.compile(TAG_TARGET_DURATION
+ ":(\\d+)\\b"); + ":(\\d+)\\b");
private static final Pattern REGEX_VERSION = Pattern.compile(TAG_VERSION + ":(\\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_MEDIA_SEQUENCE = Pattern.compile(TAG_MEDIA_SEQUENCE private static final Pattern REGEX_MEDIA_SEQUENCE = Pattern.compile(TAG_MEDIA_SEQUENCE
+ ":(\\d+)\\b"); + ":(\\d+)\\b");
private static final Pattern REGEX_MEDIA_DURATION = Pattern.compile(TAG_MEDIA_DURATION private static final Pattern REGEX_MEDIA_DURATION = Pattern.compile(TAG_MEDIA_DURATION
@ -245,6 +248,7 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
private static HlsMediaPlaylist parseMediaPlaylist(LineIterator iterator, String baseUri) private static HlsMediaPlaylist parseMediaPlaylist(LineIterator iterator, String baseUri)
throws IOException { throws IOException {
@HlsMediaPlaylist.PlaylistType int playlistType = HlsMediaPlaylist.PLAYLIST_TYPE_UNKNOWN;
long startOffsetUs = C.TIME_UNSET; long startOffsetUs = C.TIME_UNSET;
int mediaSequence = 0; int mediaSequence = 0;
int version = 1; // Default version == 1. int version = 1; // Default version == 1.
@ -270,7 +274,16 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
String line; String line;
while (iterator.hasNext()) { while (iterator.hasNext()) {
line = iterator.next(); line = iterator.next();
if (line.startsWith(TAG_START)) { if (line.startsWith(TAG_PLAYLIST_TYPE)) {
String playlistTypeString = parseStringAttr(line, REGEX_PLAYLIST_TYPE);
if ("VOD".equals(playlistTypeString)) {
playlistType = HlsMediaPlaylist.PLAYLIST_TYPE_VOD;
} else if ("EVENT".equals(playlistTypeString)) {
playlistType = HlsMediaPlaylist.PLAYLIST_TYPE_EVENT;
} else {
throw new ParserException("Illegal playlist type: " + playlistTypeString);
}
} else if (line.startsWith(TAG_START)) {
startOffsetUs = (long) (parseDoubleAttr(line, REGEX_TIME_OFFSET) * C.MICROS_PER_SECOND); startOffsetUs = (long) (parseDoubleAttr(line, REGEX_TIME_OFFSET) * C.MICROS_PER_SECOND);
} else if (line.startsWith(TAG_INIT_SEGMENT)) { } else if (line.startsWith(TAG_INIT_SEGMENT)) {
String uri = parseStringAttr(line, REGEX_URI); String uri = parseStringAttr(line, REGEX_URI);
@ -349,7 +362,7 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
hasEndTag = true; hasEndTag = true;
} }
} }
return new HlsMediaPlaylist(baseUri, startOffsetUs, playlistStartTimeUs, return new HlsMediaPlaylist(playlistType, baseUri, startOffsetUs, playlistStartTimeUs,
hasDiscontinuitySequence, playlistDiscontinuitySequence, mediaSequence, version, hasDiscontinuitySequence, playlistDiscontinuitySequence, mediaSequence, version,
targetDurationUs, hasEndTag, playlistStartTimeUs != 0, initializationSegment, segments); targetDurationUs, hasEndTag, playlistStartTimeUs != 0, initializationSegment, segments);
} }

View File

@ -450,10 +450,11 @@ public final class HlsPlaylistTracker implements Loader.Callback<ParsingLoadable
if (playlistSnapshot == null) { if (playlistSnapshot == null) {
return false; return false;
} }
// TODO: Return true for event playlists once playlist types are supported.
long currentTimeMs = SystemClock.elapsedRealtime(); long currentTimeMs = SystemClock.elapsedRealtime();
long snapshotValidityDurationMs = Math.max(30000, C.usToMs(playlistSnapshot.durationUs)); long snapshotValidityDurationMs = Math.max(30000, C.usToMs(playlistSnapshot.durationUs));
return playlistSnapshot.hasEndTag return playlistSnapshot.hasEndTag
|| playlistSnapshot.playlistType == HlsMediaPlaylist.PLAYLIST_TYPE_EVENT
|| playlistSnapshot.playlistType == HlsMediaPlaylist.PLAYLIST_TYPE_VOD
|| lastSnapshotLoadMs + snapshotValidityDurationMs > currentTimeMs; || lastSnapshotLoadMs + snapshotValidityDurationMs > currentTimeMs;
} }