Skip to content

Commit

Permalink
Defer setting defaults for rendition reports until playlist is parsed
Browse files Browse the repository at this point in the history
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: #9592
PiperOrigin-RevId: 406329684
  • Loading branch information
marcbaechinger committed Oct 29, 2021
1 parent 7975182 commit dacdf5c
Show file tree
Hide file tree
Showing 2 changed files with 135 additions and 15 deletions.
Expand Up @@ -645,7 +645,7 @@ private static HlsMediaPlaylist parseMediaPlaylist(
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;
Expand Down Expand Up @@ -854,17 +854,11 @@ private static HlsMediaPlaylist parseMediaPlaylist(
} 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;
Expand Down Expand Up @@ -1022,6 +1016,24 @@ private static HlsMediaPlaylist parseMediaPlaylist(
}
}

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);
}
Expand All @@ -1046,7 +1058,7 @@ private static HlsMediaPlaylist parseMediaPlaylist(
segments,
trailingParts,
serverControl,
renditionReports);
renditionReportMap);
}

private static DrmInitData getPlaylistProtectionSchemes(
Expand Down
Expand Up @@ -903,6 +903,114 @@ public void parseMediaPlaylist_withRenditionReportLowLatency_parseAllAttributes(
assertThat(report0.lastPartIndex).isEqualTo(2);
}

@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()
Expand All @@ -917,7 +1025,7 @@ public void parseMediaPlaylist_withRenditionReportLowLatency_parseAllAttributes(
+ "#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 =
Expand All @@ -926,7 +1034,7 @@ public void parseMediaPlaylist_withRenditionReportLowLatency_parseAllAttributes(
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);
}

Expand All @@ -945,7 +1053,7 @@ public void parseMediaPlaylist_withRenditionReportLowLatency_parseAllAttributes(
+ "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 =
Expand All @@ -955,7 +1063,7 @@ public void parseMediaPlaylist_withRenditionReportLowLatency_parseAllAttributes(
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);
}

Expand Down

0 comments on commit dacdf5c

Please sign in to comment.