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
This commit is contained in:
olly 2019-03-29 13:13:24 +00:00 committed by Toni
parent 32924e3f17
commit 2623b4b382
7 changed files with 40 additions and 41 deletions

View file

@ -78,16 +78,15 @@ public final class HlsDownloader extends SegmentDownloader<HlsPlaylist> {
@Override
protected List<Segment> getSegments(
DataSource dataSource, HlsPlaylist playlist, boolean allowIncompleteList) throws IOException {
String baseUri = playlist.baseUri;
ArrayList<DataSpec> mediaPlaylistDataSpecs = new ArrayList<>();
if (playlist instanceof HlsMasterPlaylist) {
HlsMasterPlaylist masterPlaylist = (HlsMasterPlaylist) playlist;
addMediaPlaylistDataSpecs(baseUri, masterPlaylist.variants, mediaPlaylistDataSpecs);
addMediaPlaylistDataSpecs(baseUri, masterPlaylist.audios, mediaPlaylistDataSpecs);
addMediaPlaylistDataSpecs(baseUri, masterPlaylist.subtitles, mediaPlaylistDataSpecs);
addMediaPlaylistDataSpecs(masterPlaylist.variants, mediaPlaylistDataSpecs);
addMediaPlaylistDataSpecs(masterPlaylist.audios, mediaPlaylistDataSpecs);
addMediaPlaylistDataSpecs(masterPlaylist.subtitles, mediaPlaylistDataSpecs);
} else {
mediaPlaylistDataSpecs.add(SegmentDownloader.getCompressibleDataSpec(Uri.parse(baseUri)));
mediaPlaylistDataSpecs.add(
SegmentDownloader.getCompressibleDataSpec(Uri.parse(playlist.baseUri)));
}
ArrayList<Segment> segments = new ArrayList<>();
@ -119,11 +118,9 @@ public final class HlsDownloader extends SegmentDownloader<HlsPlaylist> {
return segments;
}
private void addMediaPlaylistDataSpecs(
String baseUri, List<? extends HlsUrl> urls, List<DataSpec> out) {
private void addMediaPlaylistDataSpecs(List<? extends HlsUrl> urls, List<DataSpec> out) {
for (int i = 0; i < urls.size(); i++) {
Uri playlistUri = UriUtil.resolveToUri(baseUri, urls.get(i).url);
out.add(SegmentDownloader.getCompressibleDataSpec(playlistUri));
out.add(SegmentDownloader.getCompressibleDataSpec(urls.get(i).url));
}
}

View file

@ -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.ParsingLoadable;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.UriUtil;
import java.io.IOException;
import java.util.ArrayList;
import java.util.IdentityHashMap;
@ -454,7 +453,7 @@ public final class DefaultHlsPlaylistTracker
mediaPlaylistLoadable =
new ParsingLoadable<>(
dataSourceFactory.createDataSource(C.DATA_TYPE_MANIFEST),
UriUtil.resolveToUri(masterPlaylist.baseUri, playlistUrl.url),
playlistUrl.url,
C.DATA_TYPE_MANIFEST,
mediaPlaylistParser);
}

View file

@ -15,6 +15,7 @@
*/
package com.google.android.exoplayer2.source.hls.playlist;
import android.net.Uri;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.drm.DrmInitData;
@ -52,10 +53,8 @@ public final class HlsMasterPlaylist extends HlsPlaylist {
/** Represents a url in an HLS master playlist. */
public abstract static class HlsUrl {
/**
* The http url from which the media playlist can be obtained.
*/
public final String url;
/** The http url from which the media playlist can be obtained. */
public final Uri url;
/**
* Format information associated with the HLS url.
*/
@ -65,7 +64,7 @@ public final class HlsMasterPlaylist extends HlsPlaylist {
* @param url See {@link #url}.
* @param format See {@link #format}.
*/
public HlsUrl(String url, Format format) {
public HlsUrl(Uri url, Format format) {
this.url = url;
this.format = format;
}
@ -95,7 +94,7 @@ public final class HlsMasterPlaylist extends HlsPlaylist {
* @param captionGroupId See {@link #captionGroupId}.
*/
public Variant(
String url,
Uri url,
Format format,
@Nullable String videoGroupId,
@Nullable String audioGroupId,
@ -114,7 +113,7 @@ public final class HlsMasterPlaylist extends HlsPlaylist {
* @param url The media playlist url.
* @return The variant instance.
*/
public static Variant createMediaPlaylistVariantUrl(String url) {
public static Variant createMediaPlaylistVariantUrl(Uri url) {
Format format =
Format.createContainerFormat(
"0",
@ -151,7 +150,7 @@ public final class HlsMasterPlaylist extends HlsPlaylist {
* @param groupId See {@link #groupId}.
* @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);
this.groupId = groupId;
this.name = name;
@ -253,7 +252,7 @@ public final class HlsMasterPlaylist extends HlsPlaylist {
*/
public static HlsMasterPlaylist createSingleVariantMasterPlaylist(String variantUrl) {
List<Variant> variant =
Collections.singletonList(Variant.createMediaPlaylistVariantUrl(variantUrl));
Collections.singletonList(Variant.createMediaPlaylistVariantUrl(Uri.parse(variantUrl)));
return new HlsMasterPlaylist(
/* baseUri= */ null,
/* tags= */ Collections.emptyList(),

View file

@ -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.upstream.ParsingLoadable;
import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.UriUtil;
import com.google.android.exoplayer2.util.Util;
import java.io.BufferedReader;
import java.io.IOException;
@ -337,6 +338,7 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
line =
replaceVariableReferences(
iterator.next(), variableDefinitions); // #EXT-X-STREAM-INF's URI.
Uri uri = UriUtil.resolveToUri(baseUri, line);
Format format =
Format.createVideoContainerFormat(
/* id= */ Integer.toString(variants.size()),
@ -353,7 +355,7 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
/* roleFlags= */ 0);
Variant variant =
new Variant(
line, format, videoGroupId, audioGroupId, subtitlesGroupId, closedCaptionsGroupId);
uri, format, videoGroupId, audioGroupId, subtitlesGroupId, closedCaptionsGroupId);
variants.add(variant);
// TODO: Don't deduplicate variants by URL.
if (variantUrls.add(line)) {
@ -366,7 +368,8 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
line = mediaTags.get(i);
String groupId = parseStringAttr(line, REGEX_GROUP_ID, 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);
@C.SelectionFlags int selectionFlags = parseSelectionFlags(line);
@C.RoleFlags int roleFlags = parseRoleFlags(line, variableDefinitions);

View file

@ -88,14 +88,14 @@ public interface HlsPlaylistTracker {
final class PlaylistStuckException extends IOException {
/** The url of the stuck playlist. */
public final String url;
public final Uri url;
/**
* Creates an instance.
*
* @param url See {@link #url}.
*/
public PlaylistStuckException(String url) {
public PlaylistStuckException(Uri url) {
this.url = url;
}
}
@ -104,14 +104,14 @@ public interface HlsPlaylistTracker {
final class PlaylistResetException extends IOException {
/** The url of the reset playlist. */
public final String url;
public final Uri url;
/**
* Creates an instance.
*
* @param url See {@link #url}.
*/
public PlaylistResetException(String url) {
public PlaylistResetException(Uri url) {
this.url = url;
}
}

View file

@ -19,6 +19,7 @@ import static org.mockito.Matchers.anyInt;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import android.net.Uri;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.source.CompositeSequenceableLoaderFactory;
@ -120,7 +121,6 @@ public final class HlsMediaPeriodTest {
private static Variant createMuxedVideoAudioVariant(int bitrate) {
return createVariant(
"http://url",
Format.createVideoContainerFormat(
/* id= */ null,
/* label= */ null,
@ -138,7 +138,6 @@ public final class HlsMediaPeriodTest {
private static Variant createAudioOnlyVariant(int bitrate) {
return createVariant(
"http://url",
Format.createVideoContainerFormat(
/* id= */ null,
/* label= */ null,
@ -155,19 +154,19 @@ public final class HlsMediaPeriodTest {
}
private static Rendition createAudioRendition(String language) {
return createRendition("http://url", createAudioFormat(language), "", "");
return createRendition(createAudioFormat(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) {
return new Variant(url, format, null, null, null, null);
private static Variant createVariant(Format format) {
return new Variant(Uri.parse("https://variant"), format, null, null, null, null);
}
private static Rendition createRendition(String url, Format format, String groupId, String name) {
return new Rendition(url, format, groupId, name);
private static Rendition createRendition(Format format, String groupId, String name) {
return new Rendition(Uri.parse("https://rendition"), format, groupId, name);
}
private static Format createAudioFormat(String language) {

View file

@ -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.width).isEqualTo(304);
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.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.codecs).isNull();
assertThat(variants.get(2).format.width).isEqualTo(384);
assertThat(variants.get(2).format.height).isEqualTo(160);
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.codecs).isNull();
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.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.codecs).isEqualTo("mp4a.40.5");
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.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
@ -291,7 +292,8 @@ public class HlsMasterPlaylistParserTest {
parseMasterPlaylist(PLAYLIST_URI, PLAYLIST_WITH_VARIABLE_SUBSTITUTION);
HlsMasterPlaylist.HlsUrl variant = playlistWithSubstitutions.variants.get(0);
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)