Commit 2623b4b3 by olly Committed by Toni

Pre-resolve HlsUrl urls

This is to make it possible to use equality of HlsUrl.url fields
to determine whether two HlsUrls point at the same media playlist.
This doesn't work currently because it's possible to mix absolute
and relative urls, which will not be equal until after the relative
url is resolved against the playlist baseUrl.

Issue: #5596
Issue: #2600
PiperOrigin-RevId: 240966503
parent 32924e3f
...@@ -78,16 +78,15 @@ public final class HlsDownloader extends SegmentDownloader<HlsPlaylist> { ...@@ -78,16 +78,15 @@ public final class HlsDownloader extends SegmentDownloader<HlsPlaylist> {
@Override @Override
protected List<Segment> getSegments( protected List<Segment> getSegments(
DataSource dataSource, HlsPlaylist playlist, boolean allowIncompleteList) throws IOException { DataSource dataSource, HlsPlaylist playlist, boolean allowIncompleteList) throws IOException {
String baseUri = playlist.baseUri;
ArrayList<DataSpec> mediaPlaylistDataSpecs = new ArrayList<>(); ArrayList<DataSpec> mediaPlaylistDataSpecs = new ArrayList<>();
if (playlist instanceof HlsMasterPlaylist) { if (playlist instanceof HlsMasterPlaylist) {
HlsMasterPlaylist masterPlaylist = (HlsMasterPlaylist) playlist; HlsMasterPlaylist masterPlaylist = (HlsMasterPlaylist) playlist;
addMediaPlaylistDataSpecs(baseUri, masterPlaylist.variants, mediaPlaylistDataSpecs); addMediaPlaylistDataSpecs(masterPlaylist.variants, mediaPlaylistDataSpecs);
addMediaPlaylistDataSpecs(baseUri, masterPlaylist.audios, mediaPlaylistDataSpecs); addMediaPlaylistDataSpecs(masterPlaylist.audios, mediaPlaylistDataSpecs);
addMediaPlaylistDataSpecs(baseUri, masterPlaylist.subtitles, mediaPlaylistDataSpecs); addMediaPlaylistDataSpecs(masterPlaylist.subtitles, mediaPlaylistDataSpecs);
} else { } else {
mediaPlaylistDataSpecs.add(SegmentDownloader.getCompressibleDataSpec(Uri.parse(baseUri))); mediaPlaylistDataSpecs.add(
SegmentDownloader.getCompressibleDataSpec(Uri.parse(playlist.baseUri)));
} }
ArrayList<Segment> segments = new ArrayList<>(); ArrayList<Segment> segments = new ArrayList<>();
...@@ -119,11 +118,9 @@ public final class HlsDownloader extends SegmentDownloader<HlsPlaylist> { ...@@ -119,11 +118,9 @@ public final class HlsDownloader extends SegmentDownloader<HlsPlaylist> {
return segments; return segments;
} }
private void addMediaPlaylistDataSpecs( private void addMediaPlaylistDataSpecs(List<? extends HlsUrl> urls, List<DataSpec> out) {
String baseUri, List<? extends HlsUrl> urls, List<DataSpec> out) {
for (int i = 0; i < urls.size(); i++) { for (int i = 0; i < urls.size(); i++) {
Uri playlistUri = UriUtil.resolveToUri(baseUri, urls.get(i).url); out.add(SegmentDownloader.getCompressibleDataSpec(urls.get(i).url));
out.add(SegmentDownloader.getCompressibleDataSpec(playlistUri));
} }
} }
......
...@@ -32,7 +32,6 @@ import com.google.android.exoplayer2.upstream.Loader; ...@@ -32,7 +32,6 @@ import com.google.android.exoplayer2.upstream.Loader;
import com.google.android.exoplayer2.upstream.Loader.LoadErrorAction; import com.google.android.exoplayer2.upstream.Loader.LoadErrorAction;
import com.google.android.exoplayer2.upstream.ParsingLoadable; import com.google.android.exoplayer2.upstream.ParsingLoadable;
import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.UriUtil;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.IdentityHashMap; import java.util.IdentityHashMap;
...@@ -454,7 +453,7 @@ public final class DefaultHlsPlaylistTracker ...@@ -454,7 +453,7 @@ public final class DefaultHlsPlaylistTracker
mediaPlaylistLoadable = mediaPlaylistLoadable =
new ParsingLoadable<>( new ParsingLoadable<>(
dataSourceFactory.createDataSource(C.DATA_TYPE_MANIFEST), dataSourceFactory.createDataSource(C.DATA_TYPE_MANIFEST),
UriUtil.resolveToUri(masterPlaylist.baseUri, playlistUrl.url), playlistUrl.url,
C.DATA_TYPE_MANIFEST, C.DATA_TYPE_MANIFEST,
mediaPlaylistParser); mediaPlaylistParser);
} }
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
*/ */
package com.google.android.exoplayer2.source.hls.playlist; package com.google.android.exoplayer2.source.hls.playlist;
import android.net.Uri;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.drm.DrmInitData; import com.google.android.exoplayer2.drm.DrmInitData;
...@@ -52,10 +53,8 @@ public final class HlsMasterPlaylist extends HlsPlaylist { ...@@ -52,10 +53,8 @@ public final class HlsMasterPlaylist extends HlsPlaylist {
/** Represents a url in an HLS master playlist. */ /** Represents a url in an HLS master playlist. */
public abstract static class HlsUrl { public abstract static class HlsUrl {
/** /** The http url from which the media playlist can be obtained. */
* The http url from which the media playlist can be obtained. public final Uri url;
*/
public final String url;
/** /**
* Format information associated with the HLS url. * Format information associated with the HLS url.
*/ */
...@@ -65,7 +64,7 @@ public final class HlsMasterPlaylist extends HlsPlaylist { ...@@ -65,7 +64,7 @@ public final class HlsMasterPlaylist extends HlsPlaylist {
* @param url See {@link #url}. * @param url See {@link #url}.
* @param format See {@link #format}. * @param format See {@link #format}.
*/ */
public HlsUrl(String url, Format format) { public HlsUrl(Uri url, Format format) {
this.url = url; this.url = url;
this.format = format; this.format = format;
} }
...@@ -95,7 +94,7 @@ public final class HlsMasterPlaylist extends HlsPlaylist { ...@@ -95,7 +94,7 @@ public final class HlsMasterPlaylist extends HlsPlaylist {
* @param captionGroupId See {@link #captionGroupId}. * @param captionGroupId See {@link #captionGroupId}.
*/ */
public Variant( public Variant(
String url, Uri url,
Format format, Format format,
@Nullable String videoGroupId, @Nullable String videoGroupId,
@Nullable String audioGroupId, @Nullable String audioGroupId,
...@@ -114,7 +113,7 @@ public final class HlsMasterPlaylist extends HlsPlaylist { ...@@ -114,7 +113,7 @@ public final class HlsMasterPlaylist extends HlsPlaylist {
* @param url The media playlist url. * @param url The media playlist url.
* @return The variant instance. * @return The variant instance.
*/ */
public static Variant createMediaPlaylistVariantUrl(String url) { public static Variant createMediaPlaylistVariantUrl(Uri url) {
Format format = Format format =
Format.createContainerFormat( Format.createContainerFormat(
"0", "0",
...@@ -151,7 +150,7 @@ public final class HlsMasterPlaylist extends HlsPlaylist { ...@@ -151,7 +150,7 @@ public final class HlsMasterPlaylist extends HlsPlaylist {
* @param groupId See {@link #groupId}. * @param groupId See {@link #groupId}.
* @param name See {@link #name}. * @param name See {@link #name}.
*/ */
public Rendition(String url, Format format, String groupId, String name) { public Rendition(Uri url, Format format, String groupId, String name) {
super(url, format); super(url, format);
this.groupId = groupId; this.groupId = groupId;
this.name = name; this.name = name;
...@@ -253,7 +252,7 @@ public final class HlsMasterPlaylist extends HlsPlaylist { ...@@ -253,7 +252,7 @@ public final class HlsMasterPlaylist extends HlsPlaylist {
*/ */
public static HlsMasterPlaylist createSingleVariantMasterPlaylist(String variantUrl) { public static HlsMasterPlaylist createSingleVariantMasterPlaylist(String variantUrl) {
List<Variant> variant = List<Variant> variant =
Collections.singletonList(Variant.createMediaPlaylistVariantUrl(variantUrl)); Collections.singletonList(Variant.createMediaPlaylistVariantUrl(Uri.parse(variantUrl)));
return new HlsMasterPlaylist( return new HlsMasterPlaylist(
/* baseUri= */ null, /* baseUri= */ null,
/* tags= */ Collections.emptyList(), /* tags= */ Collections.emptyList(),
......
...@@ -31,6 +31,7 @@ import com.google.android.exoplayer2.source.hls.playlist.HlsMasterPlaylist.Varia ...@@ -31,6 +31,7 @@ import com.google.android.exoplayer2.source.hls.playlist.HlsMasterPlaylist.Varia
import com.google.android.exoplayer2.source.hls.playlist.HlsMediaPlaylist.Segment; import com.google.android.exoplayer2.source.hls.playlist.HlsMediaPlaylist.Segment;
import com.google.android.exoplayer2.upstream.ParsingLoadable; import com.google.android.exoplayer2.upstream.ParsingLoadable;
import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.UriUtil;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.IOException; import java.io.IOException;
...@@ -337,6 +338,7 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli ...@@ -337,6 +338,7 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
line = line =
replaceVariableReferences( replaceVariableReferences(
iterator.next(), variableDefinitions); // #EXT-X-STREAM-INF's URI. iterator.next(), variableDefinitions); // #EXT-X-STREAM-INF's URI.
Uri uri = UriUtil.resolveToUri(baseUri, line);
Format format = Format format =
Format.createVideoContainerFormat( Format.createVideoContainerFormat(
/* id= */ Integer.toString(variants.size()), /* id= */ Integer.toString(variants.size()),
...@@ -353,7 +355,7 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli ...@@ -353,7 +355,7 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
/* roleFlags= */ 0); /* roleFlags= */ 0);
Variant variant = Variant variant =
new Variant( new Variant(
line, format, videoGroupId, audioGroupId, subtitlesGroupId, closedCaptionsGroupId); uri, format, videoGroupId, audioGroupId, subtitlesGroupId, closedCaptionsGroupId);
variants.add(variant); variants.add(variant);
// TODO: Don't deduplicate variants by URL. // TODO: Don't deduplicate variants by URL.
if (variantUrls.add(line)) { if (variantUrls.add(line)) {
...@@ -366,7 +368,8 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli ...@@ -366,7 +368,8 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
line = mediaTags.get(i); line = mediaTags.get(i);
String groupId = parseStringAttr(line, REGEX_GROUP_ID, variableDefinitions); String groupId = parseStringAttr(line, REGEX_GROUP_ID, variableDefinitions);
String name = parseStringAttr(line, REGEX_NAME, variableDefinitions); String name = parseStringAttr(line, REGEX_NAME, variableDefinitions);
String uri = parseOptionalStringAttr(line, REGEX_URI, variableDefinitions); String referenceUri = parseOptionalStringAttr(line, REGEX_URI, variableDefinitions);
Uri uri = referenceUri == null ? null : UriUtil.resolveToUri(baseUri, referenceUri);
String language = parseOptionalStringAttr(line, REGEX_LANGUAGE, variableDefinitions); String language = parseOptionalStringAttr(line, REGEX_LANGUAGE, variableDefinitions);
@C.SelectionFlags int selectionFlags = parseSelectionFlags(line); @C.SelectionFlags int selectionFlags = parseSelectionFlags(line);
@C.RoleFlags int roleFlags = parseRoleFlags(line, variableDefinitions); @C.RoleFlags int roleFlags = parseRoleFlags(line, variableDefinitions);
......
...@@ -88,14 +88,14 @@ public interface HlsPlaylistTracker { ...@@ -88,14 +88,14 @@ public interface HlsPlaylistTracker {
final class PlaylistStuckException extends IOException { final class PlaylistStuckException extends IOException {
/** The url of the stuck playlist. */ /** The url of the stuck playlist. */
public final String url; public final Uri url;
/** /**
* Creates an instance. * Creates an instance.
* *
* @param url See {@link #url}. * @param url See {@link #url}.
*/ */
public PlaylistStuckException(String url) { public PlaylistStuckException(Uri url) {
this.url = url; this.url = url;
} }
} }
...@@ -104,14 +104,14 @@ public interface HlsPlaylistTracker { ...@@ -104,14 +104,14 @@ public interface HlsPlaylistTracker {
final class PlaylistResetException extends IOException { final class PlaylistResetException extends IOException {
/** The url of the reset playlist. */ /** The url of the reset playlist. */
public final String url; public final Uri url;
/** /**
* Creates an instance. * Creates an instance.
* *
* @param url See {@link #url}. * @param url See {@link #url}.
*/ */
public PlaylistResetException(String url) { public PlaylistResetException(Uri url) {
this.url = url; this.url = url;
} }
} }
......
...@@ -19,6 +19,7 @@ import static org.mockito.Matchers.anyInt; ...@@ -19,6 +19,7 @@ import static org.mockito.Matchers.anyInt;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import android.net.Uri;
import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.source.CompositeSequenceableLoaderFactory; import com.google.android.exoplayer2.source.CompositeSequenceableLoaderFactory;
...@@ -120,7 +121,6 @@ public final class HlsMediaPeriodTest { ...@@ -120,7 +121,6 @@ public final class HlsMediaPeriodTest {
private static Variant createMuxedVideoAudioVariant(int bitrate) { private static Variant createMuxedVideoAudioVariant(int bitrate) {
return createVariant( return createVariant(
"http://url",
Format.createVideoContainerFormat( Format.createVideoContainerFormat(
/* id= */ null, /* id= */ null,
/* label= */ null, /* label= */ null,
...@@ -138,7 +138,6 @@ public final class HlsMediaPeriodTest { ...@@ -138,7 +138,6 @@ public final class HlsMediaPeriodTest {
private static Variant createAudioOnlyVariant(int bitrate) { private static Variant createAudioOnlyVariant(int bitrate) {
return createVariant( return createVariant(
"http://url",
Format.createVideoContainerFormat( Format.createVideoContainerFormat(
/* id= */ null, /* id= */ null,
/* label= */ null, /* label= */ null,
...@@ -155,19 +154,19 @@ public final class HlsMediaPeriodTest { ...@@ -155,19 +154,19 @@ public final class HlsMediaPeriodTest {
} }
private static Rendition createAudioRendition(String language) { private static Rendition createAudioRendition(String language) {
return createRendition("http://url", createAudioFormat(language), "", ""); return createRendition(createAudioFormat(language), "", "");
} }
private static Rendition createSubtitleRendition(String language) { private static Rendition createSubtitleRendition(String language) {
return createRendition("http://url", createSubtitleFormat(language), "", ""); return createRendition(createSubtitleFormat(language), "", "");
} }
private static Variant createVariant(String url, Format format) { private static Variant createVariant(Format format) {
return new Variant(url, format, null, null, null, null); return new Variant(Uri.parse("https://variant"), format, null, null, null, null);
} }
private static Rendition createRendition(String url, Format format, String groupId, String name) { private static Rendition createRendition(Format format, String groupId, String name) {
return new Rendition(url, format, groupId, name); return new Rendition(Uri.parse("https://rendition"), format, groupId, name);
} }
private static Format createAudioFormat(String language) { private static Format createAudioFormat(String language) {
......
...@@ -158,32 +158,33 @@ public class HlsMasterPlaylistParserTest { ...@@ -158,32 +158,33 @@ public class HlsMasterPlaylistParserTest {
assertThat(variants.get(0).format.codecs).isEqualTo("mp4a.40.2,avc1.66.30"); assertThat(variants.get(0).format.codecs).isEqualTo("mp4a.40.2,avc1.66.30");
assertThat(variants.get(0).format.width).isEqualTo(304); assertThat(variants.get(0).format.width).isEqualTo(304);
assertThat(variants.get(0).format.height).isEqualTo(128); assertThat(variants.get(0).format.height).isEqualTo(128);
assertThat(variants.get(0).url).isEqualTo("http://example.com/low.m3u8"); assertThat(variants.get(0).url).isEqualTo(Uri.parse("http://example.com/low.m3u8"));
assertThat(variants.get(1).format.bitrate).isEqualTo(1280000); assertThat(variants.get(1).format.bitrate).isEqualTo(1280000);
assertThat(variants.get(1).format.codecs).isEqualTo("mp4a.40.2 , avc1.66.30 "); assertThat(variants.get(1).format.codecs).isEqualTo("mp4a.40.2 , avc1.66.30 ");
assertThat(variants.get(1).url).isEqualTo("http://example.com/spaces_in_codecs.m3u8"); assertThat(variants.get(1).url)
.isEqualTo(Uri.parse("http://example.com/spaces_in_codecs.m3u8"));
assertThat(variants.get(2).format.bitrate).isEqualTo(2560000); assertThat(variants.get(2).format.bitrate).isEqualTo(2560000);
assertThat(variants.get(2).format.codecs).isNull(); assertThat(variants.get(2).format.codecs).isNull();
assertThat(variants.get(2).format.width).isEqualTo(384); assertThat(variants.get(2).format.width).isEqualTo(384);
assertThat(variants.get(2).format.height).isEqualTo(160); assertThat(variants.get(2).format.height).isEqualTo(160);
assertThat(variants.get(2).format.frameRate).isEqualTo(25.0f); assertThat(variants.get(2).format.frameRate).isEqualTo(25.0f);
assertThat(variants.get(2).url).isEqualTo("http://example.com/mid.m3u8"); assertThat(variants.get(2).url).isEqualTo(Uri.parse("http://example.com/mid.m3u8"));
assertThat(variants.get(3).format.bitrate).isEqualTo(7680000); assertThat(variants.get(3).format.bitrate).isEqualTo(7680000);
assertThat(variants.get(3).format.codecs).isNull(); assertThat(variants.get(3).format.codecs).isNull();
assertThat(variants.get(3).format.width).isEqualTo(Format.NO_VALUE); assertThat(variants.get(3).format.width).isEqualTo(Format.NO_VALUE);
assertThat(variants.get(3).format.height).isEqualTo(Format.NO_VALUE); assertThat(variants.get(3).format.height).isEqualTo(Format.NO_VALUE);
assertThat(variants.get(3).format.frameRate).isEqualTo(29.997f); assertThat(variants.get(3).format.frameRate).isEqualTo(29.997f);
assertThat(variants.get(3).url).isEqualTo("http://example.com/hi.m3u8"); assertThat(variants.get(3).url).isEqualTo(Uri.parse("http://example.com/hi.m3u8"));
assertThat(variants.get(4).format.bitrate).isEqualTo(65000); assertThat(variants.get(4).format.bitrate).isEqualTo(65000);
assertThat(variants.get(4).format.codecs).isEqualTo("mp4a.40.5"); assertThat(variants.get(4).format.codecs).isEqualTo("mp4a.40.5");
assertThat(variants.get(4).format.width).isEqualTo(Format.NO_VALUE); assertThat(variants.get(4).format.width).isEqualTo(Format.NO_VALUE);
assertThat(variants.get(4).format.height).isEqualTo(Format.NO_VALUE); assertThat(variants.get(4).format.height).isEqualTo(Format.NO_VALUE);
assertThat(variants.get(4).format.frameRate).isEqualTo((float) Format.NO_VALUE); assertThat(variants.get(4).format.frameRate).isEqualTo((float) Format.NO_VALUE);
assertThat(variants.get(4).url).isEqualTo("http://example.com/audio-only.m3u8"); assertThat(variants.get(4).url).isEqualTo(Uri.parse("http://example.com/audio-only.m3u8"));
} }
@Test @Test
...@@ -291,7 +292,8 @@ public class HlsMasterPlaylistParserTest { ...@@ -291,7 +292,8 @@ public class HlsMasterPlaylistParserTest {
parseMasterPlaylist(PLAYLIST_URI, PLAYLIST_WITH_VARIABLE_SUBSTITUTION); parseMasterPlaylist(PLAYLIST_URI, PLAYLIST_WITH_VARIABLE_SUBSTITUTION);
HlsMasterPlaylist.HlsUrl variant = playlistWithSubstitutions.variants.get(0); HlsMasterPlaylist.HlsUrl variant = playlistWithSubstitutions.variants.get(0);
assertThat(variant.format.codecs).isEqualTo("mp4a.40.5"); assertThat(variant.format.codecs).isEqualTo("mp4a.40.5");
assertThat(variant.url).isEqualTo("http://example.com/This/{$nested}/reference/shouldnt/work"); assertThat(variant.url)
.isEqualTo(Uri.parse("http://example.com/This/{$nested}/reference/shouldnt/work"));
} }
private static HlsMasterPlaylist parseMasterPlaylist(String uri, String playlistString) private static HlsMasterPlaylist parseMasterPlaylist(String uri, String playlistString)
......
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