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
List<Segment> segments = new ArrayList<>();
List<Part> trailingParts = new ArrayList<>();
@Nullable Part preloadPart = null;
Map<Uri, RenditionReport> renditionReports = new HashMap<>();
List<RenditionReport> renditionReports = new ArrayList<>();
List<String> tags = new ArrayList<>();
long segmentDurationUs = 0;
......@@ -854,17 +854,11 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
} else if (line.equals(TAG_ENDLIST)) {
hasEndTag = true;
} else if (line.startsWith(TAG_RENDITION_REPORT)) {
long defaultValue = mediaSequence + segments.size() - (trailingParts.isEmpty() ? 1 : 0);
long lastMediaSequence = parseOptionalLongAttr(line, REGEX_LAST_MSN, defaultValue);
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);
long lastMediaSequence = parseOptionalLongAttr(line, REGEX_LAST_MSN, C.INDEX_UNSET);
int lastPartIndex = parseOptionalIntAttr(line, REGEX_LAST_PART, C.INDEX_UNSET);
String uri = parseStringAttr(line, REGEX_URI, variableDefinitions);
Uri playlistUri = Uri.parse(UriUtil.resolve(baseUri, uri));
renditionReports.put(
playlistUri, new RenditionReport(playlistUri, lastMediaSequence, lastPartIndex));
renditionReports.add(new RenditionReport(playlistUri, lastMediaSequence, lastPartIndex));
} else if (line.startsWith(TAG_PRELOAD_HINT)) {
if (preloadPart != null) {
continue;
......@@ -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) {
trailingParts.add(preloadPart);
}
......@@ -1046,7 +1058,7 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
segments,
trailingParts,
serverControl,
renditionReports);
renditionReportMap);
}
private static DrmInitData getPlaylistProtectionSchemes(
......
......@@ -905,6 +905,114 @@ public class HlsMediaPlaylistParserTest {
@Test
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()
throws IOException {
Uri playlistUri = Uri.parse("https://example.com/test.m3u8");
......@@ -917,7 +1025,7 @@ public class HlsMediaPlaylistParserTest {
+ "#EXTINF:4.00000,\n"
+ "fileSequence266.mp4\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));
HlsMediaPlaylist playlist =
......@@ -926,7 +1034,7 @@ public class HlsMediaPlaylistParserTest {
assertThat(playlist.renditionReports).hasSize(1);
HlsMediaPlaylist.RenditionReport report0 =
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);
}
......@@ -945,7 +1053,7 @@ public class HlsMediaPlaylistParserTest {
+ "fileSequence266.mp4\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-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));
HlsMediaPlaylist playlist =
......@@ -955,7 +1063,7 @@ public class HlsMediaPlaylistParserTest {
assertThat(playlist.renditionReports).hasSize(1);
HlsMediaPlaylist.RenditionReport report0 =
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);
}
......
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