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");
String playlistString = "#EXTM3U\n"
+ "#EXT-X-VERSION:3\n"
+ "#EXT-X-PLAYLIST-TYPE:VOD\n"
+ "#EXT-X-TARGETDURATION:8\n"
+ "#EXT-X-MEDIA-SEQUENCE:2679\n"
+ "#EXT-X-DISCONTINUITY-SEQUENCE:4\n"
@ -71,6 +72,7 @@ public class HlsMediaPlaylistParserTest extends TestCase {
assertEquals(HlsPlaylist.TYPE_MEDIA, playlist.type);
HlsMediaPlaylist mediaPlaylist = (HlsMediaPlaylist) playlist;
assertEquals(HlsMediaPlaylist.PLAYLIST_TYPE_VOD, mediaPlaylist.playlistType);
assertEquals(2679, mediaPlaylist.mediaSequence);
assertEquals(3, mediaPlaylist.version);

View File

@ -15,7 +15,10 @@
*/
package com.google.android.exoplayer2.source.hls.playlist;
import android.support.annotation.IntDef;
import com.google.android.exoplayer2.C;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Collections;
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 startTimeUs;
public final boolean hasDiscontinuitySequence;
@ -78,11 +93,12 @@ public final class HlsMediaPlaylist extends HlsPlaylist {
public final List<Segment> segments;
public final long durationUs;
public HlsMediaPlaylist(String baseUri, long startOffsetUs, long startTimeUs,
boolean hasDiscontinuitySequence, int discontinuitySequence, int mediaSequence, int version,
long targetDurationUs, boolean hasEndTag, boolean hasProgramDateTime,
Segment initializationSegment, List<Segment> segments) {
public HlsMediaPlaylist(@PlaylistType int playlistType, String baseUri, long startOffsetUs,
long startTimeUs, boolean hasDiscontinuitySequence, int discontinuitySequence,
int mediaSequence, int version, long targetDurationUs, boolean hasEndTag,
boolean hasProgramDateTime, Segment initializationSegment, List<Segment> segments) {
super(baseUri, HlsPlaylist.TYPE_MEDIA);
this.playlistType = playlistType;
this.startTimeUs = startTimeUs;
this.hasDiscontinuitySequence = hasDiscontinuitySequence;
this.discontinuitySequence = discontinuitySequence;
@ -137,9 +153,9 @@ public final class HlsMediaPlaylist extends HlsPlaylist {
* @return The playlist.
*/
public HlsMediaPlaylist copyWith(long startTimeUs, int discontinuitySequence) {
return new HlsMediaPlaylist(baseUri, startOffsetUs, startTimeUs, true, discontinuitySequence,
mediaSequence, version, targetDurationUs, hasEndTag, hasProgramDateTime,
initializationSegment, segments);
return new HlsMediaPlaylist(playlistType, baseUri, startOffsetUs, startTimeUs, true,
discontinuitySequence, mediaSequence, version, targetDurationUs, hasEndTag,
hasProgramDateTime, initializationSegment, segments);
}
/**
@ -152,9 +168,9 @@ public final class HlsMediaPlaylist extends HlsPlaylist {
if (this.hasEndTag) {
return this;
}
return new HlsMediaPlaylist(baseUri, startOffsetUs, startTimeUs, hasDiscontinuitySequence,
discontinuitySequence, mediaSequence, version, targetDurationUs, true, hasProgramDateTime,
initializationSegment, segments);
return new HlsMediaPlaylist(playlistType, baseUri, startOffsetUs, startTimeUs,
hasDiscontinuitySequence, discontinuitySequence, mediaSequence, version, targetDurationUs,
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 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_MEDIA = "#EXT-X-MEDIA";
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
+ ":(\\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
+ ":(\\d+)\\b");
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)
throws IOException {
@HlsMediaPlaylist.PlaylistType int playlistType = HlsMediaPlaylist.PLAYLIST_TYPE_UNKNOWN;
long startOffsetUs = C.TIME_UNSET;
int mediaSequence = 0;
int version = 1; // Default version == 1.
@ -270,7 +274,16 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
String line;
while (iterator.hasNext()) {
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);
} else if (line.startsWith(TAG_INIT_SEGMENT)) {
String uri = parseStringAttr(line, REGEX_URI);
@ -349,7 +362,7 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
hasEndTag = true;
}
}
return new HlsMediaPlaylist(baseUri, startOffsetUs, playlistStartTimeUs,
return new HlsMediaPlaylist(playlistType, baseUri, startOffsetUs, playlistStartTimeUs,
hasDiscontinuitySequence, playlistDiscontinuitySequence, mediaSequence, version,
targetDurationUs, hasEndTag, playlistStartTimeUs != 0, initializationSegment, segments);
}

View File

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