mirror of
https://github.com/samsonjs/media.git
synced 2026-04-20 13:45:47 +00:00
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:
parent
32924e3f17
commit
2623b4b382
7 changed files with 40 additions and 41 deletions
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Reference in a new issue