Commit dacdf5c4 by bachinger

Defer setting defaults for rendition reports until playlist is parsed

This makes sure that #EXT-X-RENDITION-REPORT tags can be placed before the list of segments/parts as well. We were previously assuming that these come at the end, which naturally would make sense and is done like this in all examples, but it is not explicitly defined by the spec.

Issue: google/ExoPlayer#9592
PiperOrigin-RevId: 406329684
parent 79751828
...@@ -645,7 +645,7 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli ...@@ -645,7 +645,7 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
List<Segment> segments = new ArrayList<>(); List<Segment> segments = new ArrayList<>();
List<Part> trailingParts = new ArrayList<>(); List<Part> trailingParts = new ArrayList<>();
@Nullable Part preloadPart = null; @Nullable Part preloadPart = null;
Map<Uri, RenditionReport> renditionReports = new HashMap<>(); List<RenditionReport> renditionReports = new ArrayList<>();
List<String> tags = new ArrayList<>(); List<String> tags = new ArrayList<>();
long segmentDurationUs = 0; long segmentDurationUs = 0;
...@@ -854,17 +854,11 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli ...@@ -854,17 +854,11 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
} else if (line.equals(TAG_ENDLIST)) { } else if (line.equals(TAG_ENDLIST)) {
hasEndTag = true; hasEndTag = true;
} else if (line.startsWith(TAG_RENDITION_REPORT)) { } else if (line.startsWith(TAG_RENDITION_REPORT)) {
long defaultValue = mediaSequence + segments.size() - (trailingParts.isEmpty() ? 1 : 0); long lastMediaSequence = parseOptionalLongAttr(line, REGEX_LAST_MSN, C.INDEX_UNSET);
long lastMediaSequence = parseOptionalLongAttr(line, REGEX_LAST_MSN, defaultValue); int lastPartIndex = parseOptionalIntAttr(line, REGEX_LAST_PART, C.INDEX_UNSET);
List<Part> lastParts =
trailingParts.isEmpty() ? Iterables.getLast(segments).parts : trailingParts;
int defaultPartIndex =
partTargetDurationUs != C.TIME_UNSET ? lastParts.size() - 1 : C.INDEX_UNSET;
int lastPartIndex = parseOptionalIntAttr(line, REGEX_LAST_PART, defaultPartIndex);
String uri = parseStringAttr(line, REGEX_URI, variableDefinitions); String uri = parseStringAttr(line, REGEX_URI, variableDefinitions);
Uri playlistUri = Uri.parse(UriUtil.resolve(baseUri, uri)); Uri playlistUri = Uri.parse(UriUtil.resolve(baseUri, uri));
renditionReports.put( renditionReports.add(new RenditionReport(playlistUri, lastMediaSequence, lastPartIndex));
playlistUri, new RenditionReport(playlistUri, lastMediaSequence, lastPartIndex));
} else if (line.startsWith(TAG_PRELOAD_HINT)) { } else if (line.startsWith(TAG_PRELOAD_HINT)) {
if (preloadPart != null) { if (preloadPart != null) {
continue; continue;
...@@ -1022,6 +1016,24 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli ...@@ -1022,6 +1016,24 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
} }
} }
Map<Uri, RenditionReport> renditionReportMap = new HashMap<>();
for (int i = 0; i < renditionReports.size(); i++) {
RenditionReport renditionReport = renditionReports.get(i);
long lastMediaSequence = renditionReport.lastMediaSequence;
if (lastMediaSequence == C.INDEX_UNSET) {
lastMediaSequence = mediaSequence + segments.size() - (trailingParts.isEmpty() ? 1 : 0);
}
int lastPartIndex = renditionReport.lastPartIndex;
if (lastPartIndex == C.INDEX_UNSET && partTargetDurationUs != C.TIME_UNSET) {
List<Part> lastParts =
trailingParts.isEmpty() ? Iterables.getLast(segments).parts : trailingParts;
lastPartIndex = lastParts.size() - 1;
}
renditionReportMap.put(
renditionReport.playlistUri,
new RenditionReport(renditionReport.playlistUri, lastMediaSequence, lastPartIndex));
}
if (preloadPart != null) { if (preloadPart != null) {
trailingParts.add(preloadPart); trailingParts.add(preloadPart);
} }
...@@ -1046,7 +1058,7 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli ...@@ -1046,7 +1058,7 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
segments, segments,
trailingParts, trailingParts,
serverControl, serverControl,
renditionReports); renditionReportMap);
} }
private static DrmInitData getPlaylistProtectionSchemes( private static DrmInitData getPlaylistProtectionSchemes(
......
...@@ -905,6 +905,114 @@ public class HlsMediaPlaylistParserTest { ...@@ -905,6 +905,114 @@ public class HlsMediaPlaylistParserTest {
@Test @Test
public void public void
parseMediaPlaylist_withRenditionReportBeforeSegmentsWithoutPartTargetDurationWithoutLastMsn_sameLastMsnAsCurrentPlaylist()
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-MEDIA-SEQUENCE:266\n"
+ "#EXT-X-RENDITION-REPORT:URI=\"/rendition0.m3u8\"\n"
+ "#EXTINF:4.00000,\n"
+ "fileSequence266.mp4\n";
InputStream inputStream = new ByteArrayInputStream(Util.getUtf8Bytes(playlistString));
HlsMediaPlaylist playlist =
(HlsMediaPlaylist) new HlsPlaylistParser().parse(playlistUri, inputStream);
assertThat(playlist.renditionReports).hasSize(1);
HlsMediaPlaylist.RenditionReport report =
playlist.renditionReports.get(Uri.parse("https://example.com/rendition0.m3u8"));
assertThat(report.lastMediaSequence).isEqualTo(266);
assertThat(report.lastPartIndex).isEqualTo(C.INDEX_UNSET);
}
@Test
public void
parseMediaPlaylist_withRenditionReportBeforeSegementsDefaultMsn_sameMsnAsCurrentPlaylist()
throws IOException {
Uri playlistUri = Uri.parse("https://example.com/test.m3u8");
String playlistString =
"#EXTM3U\n"
+ "#EXT-X-TARGETDURATION:4\n"
+ "#EXT-X-PART-INF:PART-TARGET=1\n"
+ "#EXT-X-VERSION:6\n"
+ "#EXT-X-MEDIA-SEQUENCE:266\n"
+ "#EXT-X-RENDITION-REPORT:URI=\"/rendition0.m3u8\",LAST-PART=2\n"
+ "#EXTINF:4.00000,\n"
+ "fileSequence266.mp4\n"
+ "#EXT-X-PART:DURATION=2.00000,URI=\"part267.0.ts\"\n";
InputStream inputStream = new ByteArrayInputStream(Util.getUtf8Bytes(playlistString));
HlsMediaPlaylist playlist =
(HlsMediaPlaylist) new HlsPlaylistParser().parse(playlistUri, inputStream);
assertThat(playlist.renditionReports).hasSize(1);
HlsMediaPlaylist.RenditionReport report =
playlist.renditionReports.get(Uri.parse("https://example.com/rendition0.m3u8"));
assertThat(report.lastMediaSequence).isEqualTo(267);
assertThat(report.lastPartIndex).isEqualTo(2);
}
@Test
public void
parseMediaPlaylist_withRenditionReportBeforeSegementsDefaultLastPart_sameLastPartIndexAsCurrentPlaylist()
throws IOException {
Uri playlistUri = Uri.parse("https://example.com/test.m3u8");
String playlistString =
"#EXTM3U\n"
+ "#EXT-X-TARGETDURATION:4\n"
+ "#EXT-X-PART-INF:PART-TARGET=1\n"
+ "#EXT-X-VERSION:6\n"
+ "#EXT-X-MEDIA-SEQUENCE:266\n"
+ "#EXT-X-RENDITION-REPORT:URI=\"/rendition0.m3u8\",LAST-MSN=267\n"
+ "#EXTINF:4.00000,\n"
+ "fileSequence266.mp4\n"
+ "#EXT-X-PART:DURATION=2.00000,URI=\"part267.0.ts\"\n"
+ "#EXT-X-PART:DURATION=2.00000,URI=\"part267.1.ts\"\n";
InputStream inputStream = new ByteArrayInputStream(Util.getUtf8Bytes(playlistString));
HlsMediaPlaylist playlist =
(HlsMediaPlaylist) new HlsPlaylistParser().parse(playlistUri, inputStream);
assertThat(playlist.renditionReports).hasSize(1);
HlsMediaPlaylist.RenditionReport report =
playlist.renditionReports.get(Uri.parse("https://example.com/rendition0.m3u8"));
assertThat(report.lastMediaSequence).isEqualTo(267);
assertThat(report.lastPartIndex).isEqualTo(1);
}
@Test
public void
parseMediaPlaylist_withRenditionReportBeforeSegements_sameMsnAndLastPartIndexAsCurrentPlaylist()
throws IOException {
Uri playlistUri = Uri.parse("https://example.com/test.m3u8");
String playlistString =
"#EXTM3U\n"
+ "#EXT-X-TARGETDURATION:4\n"
+ "#EXT-X-PART-INF:PART-TARGET=1\n"
+ "#EXT-X-VERSION:6\n"
+ "#EXT-X-MEDIA-SEQUENCE:266\n"
+ "#EXT-X-RENDITION-REPORT:URI=\"/rendition0.m3u8\"\n"
+ "#EXTINF:4.00000,\n"
+ "fileSequence266.mp4\n"
+ "#EXT-X-PART:DURATION=2.00000,URI=\"part267.0.ts\"\n"
+ "#EXT-X-PART:DURATION=2.00000,URI=\"part267.1.ts\"\n";
InputStream inputStream = new ByteArrayInputStream(Util.getUtf8Bytes(playlistString));
HlsMediaPlaylist playlist =
(HlsMediaPlaylist) new HlsPlaylistParser().parse(playlistUri, inputStream);
assertThat(playlist.renditionReports).hasSize(1);
HlsMediaPlaylist.RenditionReport report =
playlist.renditionReports.get(Uri.parse("https://example.com/rendition0.m3u8"));
assertThat(report.lastMediaSequence).isEqualTo(267);
assertThat(report.lastPartIndex).isEqualTo(1);
}
@Test
public void
parseMediaPlaylist_withRenditionReportLowLatencyWithoutLastPartIndex_sameLastPartIndexAsCurrentPlaylist() parseMediaPlaylist_withRenditionReportLowLatencyWithoutLastPartIndex_sameLastPartIndexAsCurrentPlaylist()
throws IOException { throws IOException {
Uri playlistUri = Uri.parse("https://example.com/test.m3u8"); Uri playlistUri = Uri.parse("https://example.com/test.m3u8");
...@@ -917,7 +1025,7 @@ public class HlsMediaPlaylistParserTest { ...@@ -917,7 +1025,7 @@ public class HlsMediaPlaylistParserTest {
+ "#EXTINF:4.00000,\n" + "#EXTINF:4.00000,\n"
+ "fileSequence266.mp4\n" + "fileSequence266.mp4\n"
+ "#EXT-X-PART:DURATION=2.00000,URI=\"part267.0.ts\"\n" + "#EXT-X-PART:DURATION=2.00000,URI=\"part267.0.ts\"\n"
+ "#EXT-X-RENDITION-REPORT:URI=\"/rendition0.m3u8\",LAST-MSN=100\n"; + "#EXT-X-RENDITION-REPORT:URI=\"/rendition0.m3u8\",LAST-MSN=267\n";
InputStream inputStream = new ByteArrayInputStream(Util.getUtf8Bytes(playlistString)); InputStream inputStream = new ByteArrayInputStream(Util.getUtf8Bytes(playlistString));
HlsMediaPlaylist playlist = HlsMediaPlaylist playlist =
...@@ -926,7 +1034,7 @@ public class HlsMediaPlaylistParserTest { ...@@ -926,7 +1034,7 @@ public class HlsMediaPlaylistParserTest {
assertThat(playlist.renditionReports).hasSize(1); assertThat(playlist.renditionReports).hasSize(1);
HlsMediaPlaylist.RenditionReport report0 = HlsMediaPlaylist.RenditionReport report0 =
playlist.renditionReports.get(Uri.parse("https://example.com/rendition0.m3u8")); playlist.renditionReports.get(Uri.parse("https://example.com/rendition0.m3u8"));
assertThat(report0.lastMediaSequence).isEqualTo(100); assertThat(report0.lastMediaSequence).isEqualTo(267);
assertThat(report0.lastPartIndex).isEqualTo(0); assertThat(report0.lastPartIndex).isEqualTo(0);
} }
...@@ -945,7 +1053,7 @@ public class HlsMediaPlaylistParserTest { ...@@ -945,7 +1053,7 @@ public class HlsMediaPlaylistParserTest {
+ "fileSequence266.mp4\n" + "fileSequence266.mp4\n"
+ "#EXT-X-PART:DURATION=2.00000,URI=\"part267.0.ts\"\n" + "#EXT-X-PART:DURATION=2.00000,URI=\"part267.0.ts\"\n"
+ "#EXT-X-PRELOAD-HINT:TYPE=PART,URI=\"filePart267.1.ts\"\n" + "#EXT-X-PRELOAD-HINT:TYPE=PART,URI=\"filePart267.1.ts\"\n"
+ "#EXT-X-RENDITION-REPORT:URI=\"/rendition0.m3u8\",LAST-MSN=100\n"; + "#EXT-X-RENDITION-REPORT:URI=\"/rendition0.m3u8\",LAST-MSN=267\n";
InputStream inputStream = new ByteArrayInputStream(Util.getUtf8Bytes(playlistString)); InputStream inputStream = new ByteArrayInputStream(Util.getUtf8Bytes(playlistString));
HlsMediaPlaylist playlist = HlsMediaPlaylist playlist =
...@@ -955,7 +1063,7 @@ public class HlsMediaPlaylistParserTest { ...@@ -955,7 +1063,7 @@ public class HlsMediaPlaylistParserTest {
assertThat(playlist.renditionReports).hasSize(1); assertThat(playlist.renditionReports).hasSize(1);
HlsMediaPlaylist.RenditionReport report0 = HlsMediaPlaylist.RenditionReport report0 =
playlist.renditionReports.get(Uri.parse("https://example.com/rendition0.m3u8")); playlist.renditionReports.get(Uri.parse("https://example.com/rendition0.m3u8"));
assertThat(report0.lastMediaSequence).isEqualTo(100); assertThat(report0.lastMediaSequence).isEqualTo(267);
assertThat(report0.lastPartIndex).isEqualTo(0); assertThat(report0.lastPartIndex).isEqualTo(0);
} }
......
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