Commit e4b05a5a by Oliver Woodman

Merge branch 'dev-v2' of https://github.com/JungleGenius/ExoPlayer into JungleGenius-dev-v2

parents 423cfa3b 90ee038f
...@@ -42,9 +42,11 @@ public final class HlsMediaPlaylist extends HlsPlaylist { ...@@ -42,9 +42,11 @@ public final class HlsMediaPlaylist extends HlsPlaylist {
* the media playlist does not define a media section for this segment. The same instance is * the media playlist does not define a media section for this segment. The same instance is
* used for all segments that share an EXT-X-MAP tag. * used for all segments that share an EXT-X-MAP tag.
*/ */
@Nullable public final Segment initializationSegment; public final @Nullable Segment initializationSegment;
/** The duration of the segment in microseconds, as defined by #EXTINF. */ /** The duration of the segment in microseconds, as defined by #EXTINF. */
public final long durationUs; public final long durationUs;
/** The human readable title of the segment. */
public final String title;
/** /**
* The number of #EXT-X-DISCONTINUITY tags in the playlist before the segment. * The number of #EXT-X-DISCONTINUITY tags in the playlist before the segment.
*/ */
...@@ -57,12 +59,12 @@ public final class HlsMediaPlaylist extends HlsPlaylist { ...@@ -57,12 +59,12 @@ public final class HlsMediaPlaylist extends HlsPlaylist {
* The encryption identity key uri as defined by #EXT-X-KEY, or null if the segment does not use * The encryption identity key uri as defined by #EXT-X-KEY, or null if the segment does not use
* full segment encryption with identity key. * full segment encryption with identity key.
*/ */
public final String fullSegmentEncryptionKeyUri; public final @Nullable String fullSegmentEncryptionKeyUri;
/** /**
* The encryption initialization vector as defined by #EXT-X-KEY, or null if the segment is not * The encryption initialization vector as defined by #EXT-X-KEY, or null if the segment is not
* encrypted. * encrypted.
*/ */
public final String encryptionIV; public final @Nullable String encryptionIV;
/** /**
* The segment's byte range offset, as defined by #EXT-X-BYTERANGE. * The segment's byte range offset, as defined by #EXT-X-BYTERANGE.
*/ */
...@@ -82,12 +84,24 @@ public final class HlsMediaPlaylist extends HlsPlaylist { ...@@ -82,12 +84,24 @@ public final class HlsMediaPlaylist extends HlsPlaylist {
* @param byterangeLength See {@link #byterangeLength}. * @param byterangeLength See {@link #byterangeLength}.
*/ */
public Segment(String uri, long byterangeOffset, long byterangeLength) { public Segment(String uri, long byterangeOffset, long byterangeLength) {
this(uri, null, 0, -1, C.TIME_UNSET, null, null, byterangeOffset, byterangeLength, false); this(
uri,
/* initializationSegment= */ null,
/* title= */ "",
/* durationUs= */ 0,
/* relativeDiscontinuitySequence= */ -1,
/* relativeStartTimeUs= */ C.TIME_UNSET,
/* fullSegmentEncryptionKeyUri= */ null,
/* encryptionIV= */ null,
byterangeOffset,
byterangeLength,
/* hasGapTag= */ false);
} }
/** /**
* @param url See {@link #url}. * @param url See {@link #url}.
* @param initializationSegment See {@link #initializationSegment}. * @param initializationSegment See {@link #initializationSegment}.
* @param title See {@link #title}.
* @param durationUs See {@link #durationUs}. * @param durationUs See {@link #durationUs}.
* @param relativeDiscontinuitySequence See {@link #relativeDiscontinuitySequence}. * @param relativeDiscontinuitySequence See {@link #relativeDiscontinuitySequence}.
* @param relativeStartTimeUs See {@link #relativeStartTimeUs}. * @param relativeStartTimeUs See {@link #relativeStartTimeUs}.
...@@ -99,17 +113,19 @@ public final class HlsMediaPlaylist extends HlsPlaylist { ...@@ -99,17 +113,19 @@ public final class HlsMediaPlaylist extends HlsPlaylist {
*/ */
public Segment( public Segment(
String url, String url,
Segment initializationSegment, @Nullable Segment initializationSegment,
String title,
long durationUs, long durationUs,
int relativeDiscontinuitySequence, int relativeDiscontinuitySequence,
long relativeStartTimeUs, long relativeStartTimeUs,
String fullSegmentEncryptionKeyUri, @Nullable String fullSegmentEncryptionKeyUri,
String encryptionIV, @Nullable String encryptionIV,
long byterangeOffset, long byterangeOffset,
long byterangeLength, long byterangeLength,
boolean hasGapTag) { boolean hasGapTag) {
this.url = url; this.url = url;
this.initializationSegment = initializationSegment; this.initializationSegment = initializationSegment;
this.title = title;
this.durationUs = durationUs; this.durationUs = durationUs;
this.relativeDiscontinuitySequence = relativeDiscontinuitySequence; this.relativeDiscontinuitySequence = relativeDiscontinuitySequence;
this.relativeStartTimeUs = relativeStartTimeUs; this.relativeStartTimeUs = relativeStartTimeUs;
......
...@@ -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 android.net.Uri; import android.net.Uri;
import android.support.annotation.Nullable;
import android.util.Base64; import android.util.Base64;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.Format;
...@@ -41,6 +42,7 @@ import java.util.List; ...@@ -41,6 +42,7 @@ import java.util.List;
import java.util.Queue; import java.util.Queue;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import org.checkerframework.checker.nullness.qual.PolyNull;
/** /**
* HLS playlists parsing logic. * HLS playlists parsing logic.
...@@ -106,6 +108,8 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli ...@@ -106,6 +108,8 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
+ ":(\\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
+ ":([\\d\\.]+)\\b"); + ":([\\d\\.]+)\\b");
private static final Pattern REGEX_MEDIA_TITLE =
Pattern.compile(TAG_MEDIA_DURATION + ":[\\d\\.]+\\b,(.+)");
private static final Pattern REGEX_TIME_OFFSET = Pattern.compile("TIME-OFFSET=(-?[\\d\\.]+)\\b"); private static final Pattern REGEX_TIME_OFFSET = Pattern.compile("TIME-OFFSET=(-?[\\d\\.]+)\\b");
private static final Pattern REGEX_BYTERANGE = Pattern.compile(TAG_BYTERANGE private static final Pattern REGEX_BYTERANGE = Pattern.compile(TAG_BYTERANGE
+ ":(\\d+(?:@\\d+)?)\\b"); + ":(\\d+(?:@\\d+)?)\\b");
...@@ -342,9 +346,17 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli ...@@ -342,9 +346,17 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
@C.SelectionFlags @C.SelectionFlags
private static int parseSelectionFlags(String line) { private static int parseSelectionFlags(String line) {
return (parseBooleanAttribute(line, REGEX_DEFAULT, false) ? C.SELECTION_FLAG_DEFAULT : 0) int flags = 0;
| (parseBooleanAttribute(line, REGEX_FORCED, false) ? C.SELECTION_FLAG_FORCED : 0) if (parseOptionalBooleanAttribute(line, REGEX_DEFAULT, false)) {
| (parseBooleanAttribute(line, REGEX_AUTOSELECT, false) ? C.SELECTION_FLAG_AUTOSELECT : 0); flags |= C.SELECTION_FLAG_DEFAULT;
}
if (parseOptionalBooleanAttribute(line, REGEX_FORCED, false)) {
flags |= C.SELECTION_FLAG_FORCED;
}
if (parseOptionalBooleanAttribute(line, REGEX_AUTOSELECT, false)) {
flags |= C.SELECTION_FLAG_AUTOSELECT;
}
return flags;
} }
private static HlsMediaPlaylist parseMediaPlaylist(LineIterator iterator, String baseUri) private static HlsMediaPlaylist parseMediaPlaylist(LineIterator iterator, String baseUri)
...@@ -361,6 +373,7 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli ...@@ -361,6 +373,7 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
List<String> tags = new ArrayList<>(); List<String> tags = new ArrayList<>();
long segmentDurationUs = 0; long segmentDurationUs = 0;
String segmentTitle = "";
boolean hasDiscontinuitySequence = false; boolean hasDiscontinuitySequence = false;
int playlistDiscontinuitySequence = 0; int playlistDiscontinuitySequence = 0;
int relativeDiscontinuitySequence = 0; int relativeDiscontinuitySequence = 0;
...@@ -416,6 +429,7 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli ...@@ -416,6 +429,7 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
} else if (line.startsWith(TAG_MEDIA_DURATION)) { } else if (line.startsWith(TAG_MEDIA_DURATION)) {
segmentDurationUs = segmentDurationUs =
(long) (parseDoubleAttr(line, REGEX_MEDIA_DURATION) * C.MICROS_PER_SECOND); (long) (parseDoubleAttr(line, REGEX_MEDIA_DURATION) * C.MICROS_PER_SECOND);
segmentTitle = parseOptionalStringAttr(line, REGEX_MEDIA_TITLE, "");
} else if (line.startsWith(TAG_KEY)) { } else if (line.startsWith(TAG_KEY)) {
String method = parseOptionalStringAttr(line, REGEX_METHOD); String method = parseOptionalStringAttr(line, REGEX_METHOD);
String keyFormat = parseOptionalStringAttr(line, REGEX_KEYFORMAT); String keyFormat = parseOptionalStringAttr(line, REGEX_KEYFORMAT);
...@@ -485,6 +499,7 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli ...@@ -485,6 +499,7 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
new Segment( new Segment(
line, line,
initializationSegment, initializationSegment,
segmentTitle,
segmentDurationUs, segmentDurationUs,
relativeDiscontinuitySequence, relativeDiscontinuitySequence,
segmentStartTimeUs, segmentStartTimeUs,
...@@ -495,6 +510,7 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli ...@@ -495,6 +510,7 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
hasGapTag)); hasGapTag));
segmentStartTimeUs += segmentDurationUs; segmentStartTimeUs += segmentDurationUs;
segmentDurationUs = 0; segmentDurationUs = 0;
segmentTitle = "";
if (segmentByteRangeLength != C.LENGTH_UNSET) { if (segmentByteRangeLength != C.LENGTH_UNSET) {
segmentByteRangeOffset += segmentByteRangeLength; segmentByteRangeOffset += segmentByteRangeLength;
} }
...@@ -549,11 +565,6 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli ...@@ -549,11 +565,6 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
return Double.parseDouble(parseStringAttr(line, pattern)); return Double.parseDouble(parseStringAttr(line, pattern));
} }
private static String parseOptionalStringAttr(String line, Pattern pattern) {
Matcher matcher = pattern.matcher(line);
return matcher.find() ? matcher.group(1) : null;
}
private static String parseStringAttr(String line, Pattern pattern) throws ParserException { private static String parseStringAttr(String line, Pattern pattern) throws ParserException {
Matcher matcher = pattern.matcher(line); Matcher matcher = pattern.matcher(line);
if (matcher.find() && matcher.groupCount() == 1) { if (matcher.find() && matcher.groupCount() == 1) {
...@@ -562,7 +573,18 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli ...@@ -562,7 +573,18 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
throw new ParserException("Couldn't match " + pattern.pattern() + " in " + line); throw new ParserException("Couldn't match " + pattern.pattern() + " in " + line);
} }
private static boolean parseBooleanAttribute(String line, Pattern pattern, boolean defaultValue) { private static @Nullable String parseOptionalStringAttr(String line, Pattern pattern) {
return parseOptionalStringAttr(line, pattern, null);
}
private static @PolyNull String parseOptionalStringAttr(
String line, Pattern pattern, @PolyNull String defaultValue) {
Matcher matcher = pattern.matcher(line);
return matcher.find() ? matcher.group(1) : defaultValue;
}
private static boolean parseOptionalBooleanAttribute(
String line, Pattern pattern, boolean defaultValue) {
Matcher matcher = pattern.matcher(line); Matcher matcher = pattern.matcher(line);
if (matcher.find()) { if (matcher.find()) {
return matcher.group(1).equals(BOOLEAN_TRUE); return matcher.group(1).equals(BOOLEAN_TRUE);
......
...@@ -54,18 +54,18 @@ public class HlsMediaPlaylistParserTest { ...@@ -54,18 +54,18 @@ public class HlsMediaPlaylistParserTest {
+ "\n" + "\n"
+ "#EXT-X-KEY:METHOD=AES-128," + "#EXT-X-KEY:METHOD=AES-128,"
+ "URI=\"https://priv.example.com/key.php?r=2680\",IV=0x1566B\n" + "URI=\"https://priv.example.com/key.php?r=2680\",IV=0x1566B\n"
+ "#EXTINF:7.975,\n" + "#EXTINF:7.975,segment title\n"
+ "#EXT-X-BYTERANGE:51501@2147483648\n" + "#EXT-X-BYTERANGE:51501@2147483648\n"
+ "https://priv.example.com/fileSequence2680.ts\n" + "https://priv.example.com/fileSequence2680.ts\n"
+ "\n" + "\n"
+ "#EXT-X-KEY:METHOD=NONE\n" + "#EXT-X-KEY:METHOD=NONE\n"
+ "#EXTINF:7.941,\n" + "#EXTINF:7.941,segment title .,:/# with interesting chars\n"
+ "#EXT-X-BYTERANGE:51501\n" // @2147535149 + "#EXT-X-BYTERANGE:51501\n" // @2147535149
+ "https://priv.example.com/fileSequence2681.ts\n" + "https://priv.example.com/fileSequence2681.ts\n"
+ "\n" + "\n"
+ "#EXT-X-DISCONTINUITY\n" + "#EXT-X-DISCONTINUITY\n"
+ "#EXT-X-KEY:METHOD=AES-128,URI=\"https://priv.example.com/key.php?r=2682\"\n" + "#EXT-X-KEY:METHOD=AES-128,URI=\"https://priv.example.com/key.php?r=2682\"\n"
+ "#EXTINF:7.975,\n" + "#EXTINF:7.975\n" // Trailing comma is omitted.
+ "#EXT-X-BYTERANGE:51740\n" // @2147586650 + "#EXT-X-BYTERANGE:51740\n" // @2147586650
+ "https://priv.example.com/fileSequence2682.ts\n" + "https://priv.example.com/fileSequence2682.ts\n"
+ "\n" + "\n"
...@@ -91,6 +91,7 @@ public class HlsMediaPlaylistParserTest { ...@@ -91,6 +91,7 @@ public class HlsMediaPlaylistParserTest {
assertThat(mediaPlaylist.discontinuitySequence + segment.relativeDiscontinuitySequence) assertThat(mediaPlaylist.discontinuitySequence + segment.relativeDiscontinuitySequence)
.isEqualTo(4); .isEqualTo(4);
assertThat(segment.durationUs).isEqualTo(7975000); assertThat(segment.durationUs).isEqualTo(7975000);
assertThat(segment.title).isEqualTo("");
assertThat(segment.fullSegmentEncryptionKeyUri).isNull(); assertThat(segment.fullSegmentEncryptionKeyUri).isNull();
assertThat(segment.encryptionIV).isNull(); assertThat(segment.encryptionIV).isNull();
assertThat(segment.byterangeLength).isEqualTo(51370); assertThat(segment.byterangeLength).isEqualTo(51370);
...@@ -100,6 +101,7 @@ public class HlsMediaPlaylistParserTest { ...@@ -100,6 +101,7 @@ public class HlsMediaPlaylistParserTest {
segment = segments.get(1); segment = segments.get(1);
assertThat(segment.relativeDiscontinuitySequence).isEqualTo(0); assertThat(segment.relativeDiscontinuitySequence).isEqualTo(0);
assertThat(segment.durationUs).isEqualTo(7975000); assertThat(segment.durationUs).isEqualTo(7975000);
assertThat(segment.title).isEqualTo("segment title");
assertThat(segment.fullSegmentEncryptionKeyUri) assertThat(segment.fullSegmentEncryptionKeyUri)
.isEqualTo("https://priv.example.com/key.php?r=2680"); .isEqualTo("https://priv.example.com/key.php?r=2680");
assertThat(segment.encryptionIV).isEqualTo("0x1566B"); assertThat(segment.encryptionIV).isEqualTo("0x1566B");
...@@ -110,6 +112,7 @@ public class HlsMediaPlaylistParserTest { ...@@ -110,6 +112,7 @@ public class HlsMediaPlaylistParserTest {
segment = segments.get(2); segment = segments.get(2);
assertThat(segment.relativeDiscontinuitySequence).isEqualTo(0); assertThat(segment.relativeDiscontinuitySequence).isEqualTo(0);
assertThat(segment.durationUs).isEqualTo(7941000); assertThat(segment.durationUs).isEqualTo(7941000);
assertThat(segment.title).isEqualTo("segment title .,:/# with interesting chars");
assertThat(segment.fullSegmentEncryptionKeyUri).isNull(); assertThat(segment.fullSegmentEncryptionKeyUri).isNull();
assertThat(segment.encryptionIV).isEqualTo(null); assertThat(segment.encryptionIV).isEqualTo(null);
assertThat(segment.byterangeLength).isEqualTo(51501); assertThat(segment.byterangeLength).isEqualTo(51501);
...@@ -119,6 +122,7 @@ public class HlsMediaPlaylistParserTest { ...@@ -119,6 +122,7 @@ public class HlsMediaPlaylistParserTest {
segment = segments.get(3); segment = segments.get(3);
assertThat(segment.relativeDiscontinuitySequence).isEqualTo(1); assertThat(segment.relativeDiscontinuitySequence).isEqualTo(1);
assertThat(segment.durationUs).isEqualTo(7975000); assertThat(segment.durationUs).isEqualTo(7975000);
assertThat(segment.title).isEqualTo("");
assertThat(segment.fullSegmentEncryptionKeyUri) assertThat(segment.fullSegmentEncryptionKeyUri)
.isEqualTo("https://priv.example.com/key.php?r=2682"); .isEqualTo("https://priv.example.com/key.php?r=2682");
// 0xA7A == 2682. // 0xA7A == 2682.
...@@ -131,6 +135,7 @@ public class HlsMediaPlaylistParserTest { ...@@ -131,6 +135,7 @@ public class HlsMediaPlaylistParserTest {
segment = segments.get(4); segment = segments.get(4);
assertThat(segment.relativeDiscontinuitySequence).isEqualTo(1); assertThat(segment.relativeDiscontinuitySequence).isEqualTo(1);
assertThat(segment.durationUs).isEqualTo(7975000); assertThat(segment.durationUs).isEqualTo(7975000);
assertThat(segment.title).isEqualTo("");
assertThat(segment.fullSegmentEncryptionKeyUri) assertThat(segment.fullSegmentEncryptionKeyUri)
.isEqualTo("https://priv.example.com/key.php?r=2682"); .isEqualTo("https://priv.example.com/key.php?r=2682");
// 0xA7B == 2683. // 0xA7B == 2683.
......
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