mirror of
https://github.com/samsonjs/media.git
synced 2026-04-08 11:45:51 +00:00
parent
457557b56f
commit
462fea3eaf
15 changed files with 343 additions and 146 deletions
|
|
@ -581,7 +581,7 @@ public class DashChunkSource implements ChunkSource {
|
|||
}
|
||||
if ((result & Extractor.RESULT_READ_INDEX) != 0) {
|
||||
representationHolders.get(format.id).segmentIndex =
|
||||
new DashWrappingSegmentIndex(extractor.getIndex(), uri, indexAnchor);
|
||||
new DashWrappingSegmentIndex(extractor.getIndex(), uri.toString(), indexAnchor);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,8 +19,6 @@ import com.google.android.exoplayer.chunk.parser.SegmentIndex;
|
|||
import com.google.android.exoplayer.dash.mpd.RangedUri;
|
||||
import com.google.android.exoplayer.util.Util;
|
||||
|
||||
import android.net.Uri;
|
||||
|
||||
/**
|
||||
* An implementation of {@link DashSegmentIndex} that wraps a {@link SegmentIndex} parsed from a
|
||||
* media stream.
|
||||
|
|
@ -28,16 +26,16 @@ import android.net.Uri;
|
|||
public class DashWrappingSegmentIndex implements DashSegmentIndex {
|
||||
|
||||
private final SegmentIndex segmentIndex;
|
||||
private final Uri uri;
|
||||
private final String uri;
|
||||
private final long indexAnchor;
|
||||
|
||||
/**
|
||||
* @param segmentIndex The {@link SegmentIndex} to wrap.
|
||||
* @param uri The {@link Uri} where the data is located.
|
||||
* @param uri The URI where the data is located.
|
||||
* @param indexAnchor The index anchor point. This value is added to the byte offsets specified
|
||||
* in the wrapped {@link SegmentIndex}.
|
||||
*/
|
||||
public DashWrappingSegmentIndex(SegmentIndex segmentIndex, Uri uri, long indexAnchor) {
|
||||
public DashWrappingSegmentIndex(SegmentIndex segmentIndex, String uri, long indexAnchor) {
|
||||
this.segmentIndex = segmentIndex;
|
||||
this.uri = uri;
|
||||
this.indexAnchor = indexAnchor;
|
||||
|
|
|
|||
|
|
@ -24,9 +24,9 @@ import com.google.android.exoplayer.dash.mpd.SegmentBase.SingleSegmentBase;
|
|||
import com.google.android.exoplayer.upstream.NetworkLoadable;
|
||||
import com.google.android.exoplayer.util.Assertions;
|
||||
import com.google.android.exoplayer.util.MimeTypes;
|
||||
import com.google.android.exoplayer.util.UriUtil;
|
||||
import com.google.android.exoplayer.util.Util;
|
||||
|
||||
import android.net.Uri;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import org.xml.sax.helpers.DefaultHandler;
|
||||
|
|
@ -83,7 +83,7 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
|
|||
throw new ParserException(
|
||||
"inputStream does not contain a valid media presentation description");
|
||||
}
|
||||
return parseMediaPresentationDescription(xpp, Util.parseBaseUri(connectionUrl));
|
||||
return parseMediaPresentationDescription(xpp, connectionUrl);
|
||||
} catch (XmlPullParserException e) {
|
||||
throw new ParserException(e);
|
||||
} catch (ParseException e) {
|
||||
|
|
@ -92,7 +92,7 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
|
|||
}
|
||||
|
||||
protected MediaPresentationDescription parseMediaPresentationDescription(XmlPullParser xpp,
|
||||
Uri baseUrl) throws XmlPullParserException, IOException, ParseException {
|
||||
String baseUrl) throws XmlPullParserException, IOException, ParseException {
|
||||
long availabilityStartTime = parseDateTime(xpp, "availabilityStartTime", -1);
|
||||
long durationMs = parseDuration(xpp, "mediaPresentationDuration", -1);
|
||||
long minBufferTimeMs = parseDuration(xpp, "minBufferTime", -1);
|
||||
|
|
@ -137,7 +137,7 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
|
|||
return new UtcTimingElement(schemeIdUri, value);
|
||||
}
|
||||
|
||||
protected Period parsePeriod(XmlPullParser xpp, Uri baseUrl, long mpdDurationMs)
|
||||
protected Period parsePeriod(XmlPullParser xpp, String baseUrl, long mpdDurationMs)
|
||||
throws XmlPullParserException, IOException {
|
||||
String id = xpp.getAttributeValue(null, "id");
|
||||
long startMs = parseDuration(xpp, "start", 0);
|
||||
|
|
@ -170,7 +170,7 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
|
|||
|
||||
// AdaptationSet parsing.
|
||||
|
||||
protected AdaptationSet parseAdaptationSet(XmlPullParser xpp, Uri baseUrl, long periodStartMs,
|
||||
protected AdaptationSet parseAdaptationSet(XmlPullParser xpp, String baseUrl, long periodStartMs,
|
||||
long periodDurationMs, SegmentBase segmentBase) throws XmlPullParserException, IOException {
|
||||
|
||||
String mimeType = xpp.getAttributeValue(null, "mimeType");
|
||||
|
|
@ -287,9 +287,9 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
|
|||
|
||||
// Representation parsing.
|
||||
|
||||
protected Representation parseRepresentation(XmlPullParser xpp, Uri baseUrl, long periodStartMs,
|
||||
long periodDurationMs, String mimeType, String language, SegmentBase segmentBase)
|
||||
throws XmlPullParserException, IOException {
|
||||
protected Representation parseRepresentation(XmlPullParser xpp, String baseUrl,
|
||||
long periodStartMs, long periodDurationMs, String mimeType, String language,
|
||||
SegmentBase segmentBase) throws XmlPullParserException, IOException {
|
||||
String id = xpp.getAttributeValue(null, "id");
|
||||
int bandwidth = parseInt(xpp, "bandwidth");
|
||||
int audioSamplingRate = parseInt(xpp, "audioSamplingRate");
|
||||
|
|
@ -335,7 +335,7 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
|
|||
|
||||
// SegmentBase, SegmentList and SegmentTemplate parsing.
|
||||
|
||||
protected SingleSegmentBase parseSegmentBase(XmlPullParser xpp, Uri baseUrl,
|
||||
protected SingleSegmentBase parseSegmentBase(XmlPullParser xpp, String baseUrl,
|
||||
SingleSegmentBase parent) throws XmlPullParserException, IOException {
|
||||
|
||||
long timescale = parseLong(xpp, "timescale", parent != null ? parent.timescale : 1);
|
||||
|
|
@ -364,12 +364,12 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
|
|||
}
|
||||
|
||||
protected SingleSegmentBase buildSingleSegmentBase(RangedUri initialization, long timescale,
|
||||
long presentationTimeOffset, Uri baseUrl, long indexStart, long indexLength) {
|
||||
long presentationTimeOffset, String baseUrl, long indexStart, long indexLength) {
|
||||
return new SingleSegmentBase(initialization, timescale, presentationTimeOffset, baseUrl,
|
||||
indexStart, indexLength);
|
||||
}
|
||||
|
||||
protected SegmentList parseSegmentList(XmlPullParser xpp, Uri baseUrl, SegmentList parent,
|
||||
protected SegmentList parseSegmentList(XmlPullParser xpp, String baseUrl, SegmentList parent,
|
||||
long periodDurationMs) throws XmlPullParserException, IOException {
|
||||
|
||||
long timescale = parseLong(xpp, "timescale", parent != null ? parent.timescale : 1);
|
||||
|
|
@ -413,7 +413,7 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
|
|||
startNumber, duration, timeline, segments);
|
||||
}
|
||||
|
||||
protected SegmentTemplate parseSegmentTemplate(XmlPullParser xpp, Uri baseUrl,
|
||||
protected SegmentTemplate parseSegmentTemplate(XmlPullParser xpp, String baseUrl,
|
||||
SegmentTemplate parent, long periodDurationMs) throws XmlPullParserException, IOException {
|
||||
|
||||
long timescale = parseLong(xpp, "timescale", parent != null ? parent.timescale : 1);
|
||||
|
|
@ -450,7 +450,7 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
|
|||
protected SegmentTemplate buildSegmentTemplate(RangedUri initialization, long timescale,
|
||||
long presentationTimeOffset, long periodDurationMs, int startNumber, long duration,
|
||||
List<SegmentTimelineElement> timeline, UrlTemplate initializationTemplate,
|
||||
UrlTemplate mediaTemplate, Uri baseUrl) {
|
||||
UrlTemplate mediaTemplate, String baseUrl) {
|
||||
return new SegmentTemplate(initialization, timescale, presentationTimeOffset, periodDurationMs,
|
||||
startNumber, duration, timeline, initializationTemplate, mediaTemplate, baseUrl);
|
||||
}
|
||||
|
|
@ -487,15 +487,15 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
|
|||
return defaultValue;
|
||||
}
|
||||
|
||||
protected RangedUri parseInitialization(XmlPullParser xpp, Uri baseUrl) {
|
||||
protected RangedUri parseInitialization(XmlPullParser xpp, String baseUrl) {
|
||||
return parseRangedUrl(xpp, baseUrl, "sourceURL", "range");
|
||||
}
|
||||
|
||||
protected RangedUri parseSegmentUrl(XmlPullParser xpp, Uri baseUrl) {
|
||||
protected RangedUri parseSegmentUrl(XmlPullParser xpp, String baseUrl) {
|
||||
return parseRangedUrl(xpp, baseUrl, "media", "mediaRange");
|
||||
}
|
||||
|
||||
protected RangedUri parseRangedUrl(XmlPullParser xpp, Uri baseUrl, String urlAttribute,
|
||||
protected RangedUri parseRangedUrl(XmlPullParser xpp, String baseUrl, String urlAttribute,
|
||||
String rangeAttribute) {
|
||||
String urlText = xpp.getAttributeValue(null, urlAttribute);
|
||||
long rangeStart = 0;
|
||||
|
|
@ -509,7 +509,7 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
|
|||
return buildRangedUri(baseUrl, urlText, rangeStart, rangeLength);
|
||||
}
|
||||
|
||||
protected RangedUri buildRangedUri(Uri baseUrl, String urlText, long rangeStart,
|
||||
protected RangedUri buildRangedUri(String baseUrl, String urlText, long rangeStart,
|
||||
long rangeLength) {
|
||||
return new RangedUri(baseUrl, urlText, rangeStart, rangeLength);
|
||||
}
|
||||
|
|
@ -548,15 +548,10 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
|
|||
}
|
||||
}
|
||||
|
||||
protected static Uri parseBaseUrl(XmlPullParser xpp, Uri parentBaseUrl)
|
||||
protected static String parseBaseUrl(XmlPullParser xpp, String parentBaseUrl)
|
||||
throws XmlPullParserException, IOException {
|
||||
xpp.next();
|
||||
String newBaseUrlText = xpp.getText();
|
||||
Uri newBaseUri = Uri.parse(newBaseUrlText);
|
||||
if (!newBaseUri.isAbsolute()) {
|
||||
newBaseUri = Uri.withAppendedPath(parentBaseUrl, newBaseUrlText);
|
||||
}
|
||||
return newBaseUri;
|
||||
return UriUtil.resolve(parentBaseUrl, xpp.getText());
|
||||
}
|
||||
|
||||
protected static int parseInt(XmlPullParser xpp, String name) {
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
package com.google.android.exoplayer.dash.mpd;
|
||||
|
||||
import com.google.android.exoplayer.util.Assertions;
|
||||
import com.google.android.exoplayer.util.Util;
|
||||
import com.google.android.exoplayer.util.UriUtil;
|
||||
|
||||
import android.net.Uri;
|
||||
|
||||
|
|
@ -35,31 +35,28 @@ public final class RangedUri {
|
|||
*/
|
||||
public final long length;
|
||||
|
||||
// The {@link Uri} is stored internally in two parts, {@link #baseUri} and {@link uriString}.
|
||||
// This helps optimize memory usage in the same way that DASH manifests allow many URLs to be
|
||||
// expressed concisely in the form of a single BaseURL and many relative paths. Note that this
|
||||
// optimization relies on the same {@code Uri} being passed as the {@link #baseUri} to many
|
||||
// The URI is stored internally in two parts: reference URI and a base URI to use when
|
||||
// resolving it. This helps optimize memory usage in the same way that DASH manifests allow many
|
||||
// URLs to be expressed concisely in the form of a single BaseURL and many relative paths. Note
|
||||
// that this optimization relies on the same object being passed as the base URI to many
|
||||
// instances of this class.
|
||||
private final Uri baseUri;
|
||||
private final String stringUri;
|
||||
private final String baseUri;
|
||||
private final String referenceUri;
|
||||
|
||||
private int hashCode;
|
||||
|
||||
/**
|
||||
* Constructs an ranged uri.
|
||||
* <p>
|
||||
* See {@link Util#getMergedUri(Uri, String)} for a description of how {@code baseUri} and
|
||||
* {@code stringUri} are merged.
|
||||
*
|
||||
* @param baseUri A uri that can form the base of the uri defined by the instance.
|
||||
* @param stringUri A relative or absolute uri in string form.
|
||||
* @param referenceUri A reference uri that should be resolved with respect to {@code baseUri}.
|
||||
* @param start The (zero based) index of the first byte of the range.
|
||||
* @param length The length of the range, or -1 to indicate that the range is unbounded.
|
||||
*/
|
||||
public RangedUri(Uri baseUri, String stringUri, long start, long length) {
|
||||
Assertions.checkArgument(baseUri != null || stringUri != null);
|
||||
public RangedUri(String baseUri, String referenceUri, long start, long length) {
|
||||
Assertions.checkArgument(baseUri != null || referenceUri != null);
|
||||
this.baseUri = baseUri;
|
||||
this.stringUri = stringUri;
|
||||
this.referenceUri = referenceUri;
|
||||
this.start = start;
|
||||
this.length = length;
|
||||
}
|
||||
|
|
@ -70,7 +67,16 @@ public final class RangedUri {
|
|||
* @return The {@link Uri} represented by the instance.
|
||||
*/
|
||||
public Uri getUri() {
|
||||
return Util.getMergedUri(baseUri, stringUri);
|
||||
return UriUtil.resolveToUri(baseUri, referenceUri);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the uri represented by the instance as a string.
|
||||
*
|
||||
* @return The uri represented by the instance.
|
||||
*/
|
||||
public String getUriString() {
|
||||
return UriUtil.resolve(baseUri, referenceUri);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -85,13 +91,13 @@ public final class RangedUri {
|
|||
* @return The merged {@link RangedUri} if the merge was successful. Null otherwise.
|
||||
*/
|
||||
public RangedUri attemptMerge(RangedUri other) {
|
||||
if (other == null || !getUri().equals(other.getUri())) {
|
||||
if (other == null || !getUriString().equals(other.getUriString())) {
|
||||
return null;
|
||||
} else if (length != -1 && start + length == other.start) {
|
||||
return new RangedUri(baseUri, stringUri, start,
|
||||
return new RangedUri(baseUri, referenceUri, start,
|
||||
other.length == -1 ? -1 : length + other.length);
|
||||
} else if (other.length != -1 && other.start + other.length == start) {
|
||||
return new RangedUri(baseUri, stringUri, other.start,
|
||||
return new RangedUri(baseUri, referenceUri, other.start,
|
||||
length == -1 ? -1 : other.length + length);
|
||||
} else {
|
||||
return null;
|
||||
|
|
@ -104,7 +110,7 @@ public final class RangedUri {
|
|||
int result = 17;
|
||||
result = 31 * result + (int) start;
|
||||
result = 31 * result + (int) length;
|
||||
result = 31 * result + getUri().hashCode();
|
||||
result = 31 * result + getUriString().hashCode();
|
||||
hashCode = result;
|
||||
}
|
||||
return hashCode;
|
||||
|
|
@ -121,7 +127,7 @@ public final class RangedUri {
|
|||
RangedUri other = (RangedUri) obj;
|
||||
return this.start == other.start
|
||||
&& this.length == other.length
|
||||
&& getUri().equals(other.getUri());
|
||||
&& getUriString().equals(other.getUriString());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -147,7 +147,7 @@ public abstract class Representation {
|
|||
public static class SingleSegmentRepresentation extends Representation {
|
||||
|
||||
/**
|
||||
* The {@link Uri} of the single segment.
|
||||
* The uri of the single segment.
|
||||
*/
|
||||
public final Uri uri;
|
||||
|
||||
|
|
@ -174,7 +174,7 @@ public abstract class Representation {
|
|||
* @param contentLength The content length, or -1 if unknown.
|
||||
*/
|
||||
public static SingleSegmentRepresentation newInstance(long periodStartMs, long periodDurationMs,
|
||||
String contentId, long revisionId, Format format, Uri uri, long initializationStart,
|
||||
String contentId, long revisionId, Format format, String uri, long initializationStart,
|
||||
long initializationEnd, long indexStart, long indexEnd, long contentLength) {
|
||||
RangedUri rangedUri = new RangedUri(uri, null, initializationStart,
|
||||
initializationEnd - initializationStart + 1);
|
||||
|
|
@ -197,13 +197,13 @@ public abstract class Representation {
|
|||
public SingleSegmentRepresentation(long periodStartMs, long periodDurationMs, String contentId,
|
||||
long revisionId, Format format, SingleSegmentBase segmentBase, long contentLength) {
|
||||
super(periodStartMs, periodDurationMs, contentId, revisionId, format, segmentBase);
|
||||
this.uri = segmentBase.uri;
|
||||
this.uri = Uri.parse(segmentBase.uri);
|
||||
this.indexUri = segmentBase.getIndex();
|
||||
this.contentLength = contentLength;
|
||||
// If we have an index uri then the index is defined externally, and we shouldn't return one
|
||||
// directly. If we don't, then we can't do better than an index defining a single segment.
|
||||
segmentIndex = indexUri != null ? null : new DashSingleSegmentIndex(periodStartMs * 1000,
|
||||
periodDurationMs * 1000, new RangedUri(uri, null, 0, -1));
|
||||
periodDurationMs * 1000, new RangedUri(segmentBase.uri, null, 0, -1));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -19,8 +19,6 @@ import com.google.android.exoplayer.C;
|
|||
import com.google.android.exoplayer.dash.DashSegmentIndex;
|
||||
import com.google.android.exoplayer.util.Util;
|
||||
|
||||
import android.net.Uri;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
|
|
@ -73,7 +71,7 @@ public abstract class SegmentBase {
|
|||
/**
|
||||
* The uri of the segment.
|
||||
*/
|
||||
public final Uri uri;
|
||||
public final String uri;
|
||||
|
||||
/* package */ final long indexStart;
|
||||
/* package */ final long indexLength;
|
||||
|
|
@ -89,7 +87,7 @@ public abstract class SegmentBase {
|
|||
* @param indexLength The length of the index data in bytes.
|
||||
*/
|
||||
public SingleSegmentBase(RangedUri initialization, long timescale, long presentationTimeOffset,
|
||||
Uri uri, long indexStart, long indexLength) {
|
||||
String uri, long indexStart, long indexLength) {
|
||||
super(initialization, timescale, presentationTimeOffset);
|
||||
this.uri = uri;
|
||||
this.indexStart = indexStart;
|
||||
|
|
@ -99,7 +97,7 @@ public abstract class SegmentBase {
|
|||
/**
|
||||
* @param uri The uri of the segment.
|
||||
*/
|
||||
public SingleSegmentBase(Uri uri) {
|
||||
public SingleSegmentBase(String uri) {
|
||||
this(null, 1, 0, uri, 0, -1);
|
||||
}
|
||||
|
||||
|
|
@ -289,7 +287,7 @@ public abstract class SegmentBase {
|
|||
/* package */ final UrlTemplate initializationTemplate;
|
||||
/* package */ final UrlTemplate mediaTemplate;
|
||||
|
||||
private final Uri baseUrl;
|
||||
private final String baseUrl;
|
||||
|
||||
/**
|
||||
* @param initialization A {@link RangedUri} corresponding to initialization data, if such data
|
||||
|
|
@ -315,7 +313,7 @@ public abstract class SegmentBase {
|
|||
public SegmentTemplate(RangedUri initialization, long timescale, long presentationTimeOffset,
|
||||
long periodDurationMs, int startNumber, long duration,
|
||||
List<SegmentTimelineElement> segmentTimeline, UrlTemplate initializationTemplate,
|
||||
UrlTemplate mediaTemplate, Uri baseUrl) {
|
||||
UrlTemplate mediaTemplate, String baseUrl) {
|
||||
super(initialization, timescale, presentationTimeOffset, periodDurationMs, startNumber,
|
||||
duration, segmentTimeline);
|
||||
this.initializationTemplate = initializationTemplate;
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ import com.google.android.exoplayer.upstream.DataSource;
|
|||
import com.google.android.exoplayer.upstream.DataSpec;
|
||||
import com.google.android.exoplayer.upstream.HttpDataSource.InvalidResponseCodeException;
|
||||
import com.google.android.exoplayer.util.Assertions;
|
||||
import com.google.android.exoplayer.util.UriUtil;
|
||||
import com.google.android.exoplayer.util.Util;
|
||||
|
||||
import android.net.Uri;
|
||||
|
|
@ -121,7 +122,7 @@ public class HlsChunkSource {
|
|||
private final Variant[] enabledVariants;
|
||||
private final BandwidthMeter bandwidthMeter;
|
||||
private final int adaptiveMode;
|
||||
private final Uri baseUri;
|
||||
private final String baseUri;
|
||||
private final int maxWidth;
|
||||
private final int maxHeight;
|
||||
private final int targetBufferSize;
|
||||
|
|
@ -301,11 +302,11 @@ public class HlsChunkSource {
|
|||
}
|
||||
|
||||
HlsMediaPlaylist.Segment segment = mediaPlaylist.segments.get(chunkIndex);
|
||||
Uri chunkUri = Util.getMergedUri(mediaPlaylist.baseUri, segment.url);
|
||||
Uri chunkUri = UriUtil.resolveToUri(mediaPlaylist.baseUri, segment.url);
|
||||
|
||||
// Check if encryption is specified.
|
||||
if (HlsMediaPlaylist.ENCRYPTION_METHOD_AES_128.equals(segment.encryptionMethod)) {
|
||||
Uri keyUri = Util.getMergedUri(mediaPlaylist.baseUri, segment.encryptionKeyUri);
|
||||
Uri keyUri = UriUtil.resolveToUri(mediaPlaylist.baseUri, segment.encryptionKeyUri);
|
||||
if (!keyUri.equals(encryptionKeyUri)) {
|
||||
// Encryption is specified and the key has changed.
|
||||
HlsChunk toReturn = newEncryptionKeyChunk(keyUri, segment.encryptionIV);
|
||||
|
|
@ -437,7 +438,7 @@ public class HlsChunkSource {
|
|||
}
|
||||
|
||||
private MediaPlaylistChunk newMediaPlaylistChunk(int variantIndex) {
|
||||
Uri mediaPlaylistUri = Util.getMergedUri(baseUri, enabledVariants[variantIndex].url);
|
||||
Uri mediaPlaylistUri = UriUtil.resolveToUri(baseUri, enabledVariants[variantIndex].url);
|
||||
DataSpec dataSpec = new DataSpec(mediaPlaylistUri, 0, C.LENGTH_UNBOUNDED, null,
|
||||
DataSpec.FLAG_ALLOW_GZIP);
|
||||
return new MediaPlaylistChunk(variantIndex, upstreamDataSource, dataSpec,
|
||||
|
|
|
|||
|
|
@ -15,8 +15,6 @@
|
|||
*/
|
||||
package com.google.android.exoplayer.hls;
|
||||
|
||||
import android.net.Uri;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
|
|
@ -26,7 +24,7 @@ public final class HlsMasterPlaylist extends HlsPlaylist {
|
|||
|
||||
public final List<Variant> variants;
|
||||
|
||||
public HlsMasterPlaylist(Uri baseUri, List<Variant> variants) {
|
||||
public HlsMasterPlaylist(String baseUri, List<Variant> variants) {
|
||||
super(baseUri, HlsPlaylist.TYPE_MASTER);
|
||||
this.variants = variants;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,8 +17,6 @@ package com.google.android.exoplayer.hls;
|
|||
|
||||
import com.google.android.exoplayer.C;
|
||||
|
||||
import android.net.Uri;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
|
|
@ -70,7 +68,7 @@ public final class HlsMediaPlaylist extends HlsPlaylist {
|
|||
public final boolean live;
|
||||
public final long durationUs;
|
||||
|
||||
public HlsMediaPlaylist(Uri baseUri, int mediaSequence, int targetDurationSecs, int version,
|
||||
public HlsMediaPlaylist(String baseUri, int mediaSequence, int targetDurationSecs, int version,
|
||||
boolean live, List<Segment> segments) {
|
||||
super(baseUri, HlsPlaylist.TYPE_MEDIA);
|
||||
this.mediaSequence = mediaSequence;
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@
|
|||
*/
|
||||
package com.google.android.exoplayer.hls;
|
||||
|
||||
import android.net.Uri;
|
||||
|
||||
/**
|
||||
* Represents an HLS playlist.
|
||||
|
|
@ -25,10 +24,10 @@ public abstract class HlsPlaylist {
|
|||
public final static int TYPE_MASTER = 0;
|
||||
public final static int TYPE_MEDIA = 1;
|
||||
|
||||
public final Uri baseUri;
|
||||
public final String baseUri;
|
||||
public final int type;
|
||||
|
||||
protected HlsPlaylist(Uri baseUri, int type) {
|
||||
protected HlsPlaylist(String baseUri, int type) {
|
||||
this.baseUri = baseUri;
|
||||
this.type = type;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,9 +19,6 @@ import com.google.android.exoplayer.C;
|
|||
import com.google.android.exoplayer.ParserException;
|
||||
import com.google.android.exoplayer.hls.HlsMediaPlaylist.Segment;
|
||||
import com.google.android.exoplayer.upstream.NetworkLoadable;
|
||||
import com.google.android.exoplayer.util.Util;
|
||||
|
||||
import android.net.Uri;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
|
|
@ -86,7 +83,6 @@ public final class HlsPlaylistParser implements NetworkLoadable.Parser<HlsPlayli
|
|||
@Override
|
||||
public HlsPlaylist parse(String connectionUrl, InputStream inputStream)
|
||||
throws IOException, ParserException {
|
||||
Uri baseUri = Util.parseBaseUri(connectionUrl);
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
|
||||
Queue<String> extraLines = new LinkedList<String>();
|
||||
String line;
|
||||
|
|
@ -97,7 +93,7 @@ public final class HlsPlaylistParser implements NetworkLoadable.Parser<HlsPlayli
|
|||
// Do nothing.
|
||||
} else if (line.startsWith(STREAM_INF_TAG)) {
|
||||
extraLines.add(line);
|
||||
return parseMasterPlaylist(new LineIterator(extraLines, reader), baseUri);
|
||||
return parseMasterPlaylist(new LineIterator(extraLines, reader), connectionUrl);
|
||||
} else if (line.startsWith(TARGET_DURATION_TAG)
|
||||
|| line.startsWith(MEDIA_SEQUENCE_TAG)
|
||||
|| line.startsWith(MEDIA_DURATION_TAG)
|
||||
|
|
@ -106,7 +102,7 @@ public final class HlsPlaylistParser implements NetworkLoadable.Parser<HlsPlayli
|
|||
|| line.equals(DISCONTINUITY_TAG)
|
||||
|| line.equals(ENDLIST_TAG)) {
|
||||
extraLines.add(line);
|
||||
return parseMediaPlaylist(new LineIterator(extraLines, reader), baseUri);
|
||||
return parseMediaPlaylist(new LineIterator(extraLines, reader), connectionUrl);
|
||||
} else if (line.startsWith(VERSION_TAG)) {
|
||||
extraLines.add(line);
|
||||
} else if (!line.startsWith("#")) {
|
||||
|
|
@ -119,7 +115,7 @@ public final class HlsPlaylistParser implements NetworkLoadable.Parser<HlsPlayli
|
|||
throw new ParserException("Failed to parse the playlist, could not identify any tags.");
|
||||
}
|
||||
|
||||
private static HlsMasterPlaylist parseMasterPlaylist(LineIterator iterator, Uri baseUri)
|
||||
private static HlsMasterPlaylist parseMasterPlaylist(LineIterator iterator, String baseUri)
|
||||
throws IOException {
|
||||
List<Variant> variants = new ArrayList<Variant>();
|
||||
int bandwidth = 0;
|
||||
|
|
@ -160,7 +156,7 @@ public final class HlsPlaylistParser implements NetworkLoadable.Parser<HlsPlayli
|
|||
return new HlsMasterPlaylist(baseUri, Collections.unmodifiableList(variants));
|
||||
}
|
||||
|
||||
private static HlsMediaPlaylist parseMediaPlaylist(LineIterator iterator, Uri baseUri)
|
||||
private static HlsMediaPlaylist parseMediaPlaylist(LineIterator iterator, String baseUri)
|
||||
throws IOException {
|
||||
int mediaSequence = 0;
|
||||
int targetDurationSecs = 0;
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ package com.google.android.exoplayer.smoothstreaming;
|
|||
|
||||
import com.google.android.exoplayer.C;
|
||||
import com.google.android.exoplayer.util.Assertions;
|
||||
import com.google.android.exoplayer.util.UriUtil;
|
||||
import com.google.android.exoplayer.util.Util;
|
||||
|
||||
import android.net.Uri;
|
||||
|
|
@ -197,14 +198,14 @@ public class SmoothStreamingManifest {
|
|||
public final TrackElement[] tracks;
|
||||
public final int chunkCount;
|
||||
|
||||
private final Uri baseUri;
|
||||
private final String baseUri;
|
||||
private final String chunkTemplate;
|
||||
|
||||
private final List<Long> chunkStartTimes;
|
||||
private final long[] chunkStartTimesUs;
|
||||
private final long lastChunkDurationUs;
|
||||
|
||||
public StreamElement(Uri baseUri, String chunkTemplate, int type, String subType,
|
||||
public StreamElement(String baseUri, String chunkTemplate, int type, String subType,
|
||||
long timescale, String name, int qualityLevels, int maxWidth, int maxHeight,
|
||||
int displayWidth, int displayHeight, String language, TrackElement[] tracks,
|
||||
List<Long> chunkStartTimes, long lastChunkDuration) {
|
||||
|
|
@ -274,7 +275,7 @@ public class SmoothStreamingManifest {
|
|||
String chunkUrl = chunkTemplate
|
||||
.replace(URL_PLACEHOLDER_BITRATE, Integer.toString(tracks[track].bitrate))
|
||||
.replace(URL_PLACEHOLDER_START_TIME, chunkStartTimes.get(chunkIndex).toString());
|
||||
return Util.getMergedUri(baseUri, chunkUrl);
|
||||
return UriUtil.resolveToUri(baseUri, chunkUrl);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,9 +23,7 @@ import com.google.android.exoplayer.upstream.NetworkLoadable;
|
|||
import com.google.android.exoplayer.util.Assertions;
|
||||
import com.google.android.exoplayer.util.CodecSpecificDataUtil;
|
||||
import com.google.android.exoplayer.util.MimeTypes;
|
||||
import com.google.android.exoplayer.util.Util;
|
||||
|
||||
import android.net.Uri;
|
||||
import android.util.Base64;
|
||||
import android.util.Pair;
|
||||
|
||||
|
|
@ -65,8 +63,8 @@ public class SmoothStreamingManifestParser implements
|
|||
try {
|
||||
XmlPullParser xmlParser = xmlParserFactory.newPullParser();
|
||||
xmlParser.setInput(inputStream, null);
|
||||
SmoothStreamMediaParser smoothStreamMediaParser = new SmoothStreamMediaParser(null,
|
||||
Util.parseBaseUri(connectionUrl));
|
||||
SmoothStreamMediaParser smoothStreamMediaParser =
|
||||
new SmoothStreamMediaParser(null, connectionUrl);
|
||||
return (SmoothStreamingManifest) smoothStreamMediaParser.parse(xmlParser);
|
||||
} catch (XmlPullParserException e) {
|
||||
throw new ParserException(e);
|
||||
|
|
@ -89,13 +87,13 @@ public class SmoothStreamingManifestParser implements
|
|||
*/
|
||||
private static abstract class ElementParser {
|
||||
|
||||
private final Uri baseUri;
|
||||
private final String baseUri;
|
||||
private final String tag;
|
||||
|
||||
private final ElementParser parent;
|
||||
private final List<Pair<String, Object>> normalizedAttributes;
|
||||
|
||||
public ElementParser(ElementParser parent, Uri baseUri, String tag) {
|
||||
public ElementParser(ElementParser parent, String baseUri, String tag) {
|
||||
this.parent = parent;
|
||||
this.baseUri = baseUri;
|
||||
this.tag = tag;
|
||||
|
|
@ -158,7 +156,7 @@ public class SmoothStreamingManifestParser implements
|
|||
}
|
||||
}
|
||||
|
||||
private ElementParser newChildParser(ElementParser parent, String name, Uri baseUri) {
|
||||
private ElementParser newChildParser(ElementParser parent, String name, String baseUri) {
|
||||
if (TrackElementParser.TAG.equals(name)) {
|
||||
return new TrackElementParser(parent, baseUri);
|
||||
} else if (ProtectionElementParser.TAG.equals(name)) {
|
||||
|
|
@ -342,7 +340,7 @@ public class SmoothStreamingManifestParser implements
|
|||
private ProtectionElement protectionElement;
|
||||
private List<StreamElement> streamElements;
|
||||
|
||||
public SmoothStreamMediaParser(ElementParser parent, Uri baseUri) {
|
||||
public SmoothStreamMediaParser(ElementParser parent, String baseUri) {
|
||||
super(parent, baseUri, TAG);
|
||||
lookAheadCount = -1;
|
||||
protectionElement = null;
|
||||
|
|
@ -392,7 +390,7 @@ public class SmoothStreamingManifestParser implements
|
|||
private UUID uuid;
|
||||
private byte[] initData;
|
||||
|
||||
public ProtectionElementParser(ElementParser parent, Uri baseUri) {
|
||||
public ProtectionElementParser(ElementParser parent, String baseUri) {
|
||||
super(parent, baseUri, TAG);
|
||||
}
|
||||
|
||||
|
|
@ -455,7 +453,7 @@ public class SmoothStreamingManifestParser implements
|
|||
private static final String KEY_FRAGMENT_START_TIME = "t";
|
||||
private static final String KEY_FRAGMENT_REPEAT_COUNT = "r";
|
||||
|
||||
private final Uri baseUri;
|
||||
private final String baseUri;
|
||||
private final List<TrackElement> tracks;
|
||||
|
||||
private int type;
|
||||
|
|
@ -473,7 +471,7 @@ public class SmoothStreamingManifestParser implements
|
|||
|
||||
private long lastChunkDuration;
|
||||
|
||||
public StreamElementParser(ElementParser parent, Uri baseUri) {
|
||||
public StreamElementParser(ElementParser parent, String baseUri) {
|
||||
super(parent, baseUri, TAG);
|
||||
this.baseUri = baseUri;
|
||||
tracks = new LinkedList<TrackElement>();
|
||||
|
|
@ -615,7 +613,7 @@ public class SmoothStreamingManifestParser implements
|
|||
private int nalUnitLengthField;
|
||||
private String content;
|
||||
|
||||
public TrackElementParser(ElementParser parent, Uri baseUri) {
|
||||
public TrackElementParser(ElementParser parent, String baseUri) {
|
||||
super(parent, baseUri, TAG);
|
||||
this.csd = new LinkedList<byte[]>();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,258 @@
|
|||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.google.android.exoplayer.util;
|
||||
|
||||
import android.net.Uri;
|
||||
import android.text.TextUtils;
|
||||
|
||||
/**
|
||||
* Utility methods for manipulating URIs.
|
||||
*/
|
||||
public final class UriUtil {
|
||||
|
||||
/**
|
||||
* The length of arrays returned by {@link #getUriIndices(String)}.
|
||||
*/
|
||||
private static final int INDEX_COUNT = 4;
|
||||
/**
|
||||
* An index into an array returned by {@link #getUriIndices(String)}.
|
||||
* <p>
|
||||
* The value at this position in the array is the index of the ':' after the scheme. Equals -1 if
|
||||
* the URI is a relative reference (no scheme). The hier-part starts at (schemeColon + 1),
|
||||
* including when the URI has no scheme.
|
||||
*/
|
||||
private static final int SCHEME_COLON = 0;
|
||||
/**
|
||||
* An index into an array returned by {@link #getUriIndices(String)}.
|
||||
* <p>
|
||||
* The value at this position in the array is the index of the path part. Equals (schemeColon + 1)
|
||||
* if no authority part, (schemeColon + 3) if the authority part consists of just "//", and
|
||||
* (query) if no path part. The characters starting at this index can be "//" only if the
|
||||
* authority part is non-empty (in this case the double-slash means the first segment is empty).
|
||||
*/
|
||||
private static final int PATH = 1;
|
||||
/**
|
||||
* An index into an array returned by {@link #getUriIndices(String)}.
|
||||
* <p>
|
||||
* The value at this position in the array is the index of the query part, including the '?'
|
||||
* before the query. Equals fragment if no query part, and (fragment - 1) if the query part is a
|
||||
* single '?' with no data.
|
||||
*/
|
||||
private static final int QUERY = 2;
|
||||
/**
|
||||
* An index into an array returned by {@link #getUriIndices(String)}.
|
||||
* <p>
|
||||
* The value at this position in the array is the index of the fragment part, including the '#'
|
||||
* before the fragment. Equal to the length of the URI if no fragment part, and (length - 1) if
|
||||
* the fragment part is a single '#' with no data.
|
||||
*/
|
||||
private static final int FRAGMENT = 3;
|
||||
|
||||
private UriUtil() {}
|
||||
|
||||
/**
|
||||
* Like {@link #resolve(String, String)}, but returns a {@link Uri} instead of a {@link String}.
|
||||
*
|
||||
* @param baseUri The base URI.
|
||||
* @param referenceUri The reference URI to resolve.
|
||||
*/
|
||||
public static Uri resolveToUri(String baseUri, String referenceUri) {
|
||||
return Uri.parse(resolve(baseUri, referenceUri));
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs relative resolution of a {@code referenceUri} with respect to a {@code baseUri}.
|
||||
* <p>
|
||||
* The resolution is performed as specified by RFC-3986.
|
||||
*
|
||||
* @param baseUri The base URI.
|
||||
* @param referenceUri The reference URI to resolve.
|
||||
*/
|
||||
public static String resolve(String baseUri, String referenceUri) {
|
||||
StringBuilder uri = new StringBuilder();
|
||||
|
||||
// Map null onto empty string, to make the following logic simpler.
|
||||
baseUri = baseUri == null ? "" : baseUri;
|
||||
referenceUri = referenceUri == null ? "" : referenceUri;
|
||||
|
||||
int[] refIndices = getUriIndices(referenceUri);
|
||||
if (refIndices[SCHEME_COLON] != -1) {
|
||||
// The reference is absolute. The target Uri is the reference.
|
||||
uri.append(referenceUri);
|
||||
removeDotSegments(uri, refIndices[PATH], refIndices[QUERY]);
|
||||
return uri.toString();
|
||||
}
|
||||
|
||||
int[] baseIndices = getUriIndices(baseUri);
|
||||
if (refIndices[FRAGMENT] == 0) {
|
||||
// The reference is empty or contains just the fragment part, then the target Uri is the
|
||||
// concatenation of the base Uri without its fragment, and the reference.
|
||||
return uri.append(baseUri, 0, baseIndices[FRAGMENT]).append(referenceUri).toString();
|
||||
}
|
||||
|
||||
if (refIndices[QUERY] == 0) {
|
||||
// The reference starts with the query part. The target is the base up to (but excluding) the
|
||||
// query, plus the reference.
|
||||
return uri.append(baseUri, 0, baseIndices[QUERY]).append(referenceUri).toString();
|
||||
}
|
||||
|
||||
if (refIndices[PATH] != 0) {
|
||||
// The reference has authority. The target is the base scheme plus the reference.
|
||||
int baseLimit = baseIndices[SCHEME_COLON] + 1;
|
||||
uri.append(baseUri, 0, baseLimit).append(referenceUri);
|
||||
return removeDotSegments(uri, baseLimit + refIndices[PATH], baseLimit + refIndices[QUERY]);
|
||||
}
|
||||
|
||||
if (refIndices[PATH] != refIndices[QUERY] && referenceUri.charAt(refIndices[PATH]) == '/') {
|
||||
// The reference path is rooted. The target is the base scheme and authority (if any), plus
|
||||
// the reference.
|
||||
uri.append(baseUri, 0, baseIndices[PATH]).append(referenceUri);
|
||||
return removeDotSegments(uri, baseIndices[PATH], baseIndices[PATH] + refIndices[QUERY]);
|
||||
}
|
||||
|
||||
// The target Uri is the concatenation of the base Uri up to (but excluding) the last segment,
|
||||
// and the reference. This can be split into 2 cases:
|
||||
if (baseIndices[SCHEME_COLON] + 2 < baseIndices[PATH]
|
||||
&& baseIndices[PATH] == baseIndices[QUERY]) {
|
||||
// Case 1: The base hier-part is just the authority, with an empty path. An additional '/' is
|
||||
// needed after the authority, before appending the reference.
|
||||
uri.append(baseUri, 0, baseIndices[PATH]).append('/').append(referenceUri);
|
||||
return removeDotSegments(uri, baseIndices[PATH], baseIndices[PATH] + refIndices[QUERY] + 1);
|
||||
} else {
|
||||
// Case 2: Otherwise, find the last '/' in the base hier-part and append the reference after
|
||||
// it. If base hier-part has no '/', it could only mean that it is completely empty or
|
||||
// contains only one segment, in which case the whole hier-part is excluded and the reference
|
||||
// is appended right after the base scheme colon without an added '/'.
|
||||
int lastSlashIndex = baseUri.lastIndexOf('/', baseIndices[QUERY] - 1);
|
||||
int baseLimit = lastSlashIndex == -1 ? baseIndices[PATH] : lastSlashIndex + 1;
|
||||
uri.append(baseUri, 0, baseLimit).append(referenceUri);
|
||||
return removeDotSegments(uri, baseIndices[PATH], baseLimit + refIndices[QUERY]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes dot segments from the path of a URI.
|
||||
*
|
||||
* @param uri A {@link StringBuilder} containing the URI.
|
||||
* @param offset The index of the start of the path in {@code uri}.
|
||||
* @param limit The limit (exclusive) of the path in {@code uri}.
|
||||
*/
|
||||
private static String removeDotSegments(StringBuilder uri, int offset, int limit) {
|
||||
if (offset >= limit) {
|
||||
// Nothing to do.
|
||||
return uri.toString();
|
||||
}
|
||||
if (uri.charAt(offset) == '/') {
|
||||
// If the path starts with a /, always retain it.
|
||||
offset++;
|
||||
}
|
||||
// The first character of the current path segment.
|
||||
int segmentStart = offset;
|
||||
int i = offset;
|
||||
while (i <= limit) {
|
||||
int nextSegmentStart = -1;
|
||||
if (i == limit) {
|
||||
nextSegmentStart = i;
|
||||
} else if (uri.charAt(i) == '/') {
|
||||
nextSegmentStart = i + 1;
|
||||
} else {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
// We've encountered the end of a segment or the end of the path. If the final segment was
|
||||
// "." or "..", remove the appropriate segments of the path.
|
||||
if (i == segmentStart + 1 && uri.charAt(segmentStart) == '.') {
|
||||
// Given "abc/def/./ghi", remove "./" to get "abc/def/ghi".
|
||||
uri.delete(segmentStart, nextSegmentStart);
|
||||
limit -= nextSegmentStart - segmentStart;
|
||||
i = segmentStart;
|
||||
} else if (i == segmentStart + 2 && uri.charAt(segmentStart) == '.'
|
||||
&& uri.charAt(segmentStart + 1) == '.') {
|
||||
// Given "abc/def/../ghi", remove "def/../" to get "abc/ghi".
|
||||
int prevSegmentStart = uri.lastIndexOf("/", segmentStart - 2) + 1;
|
||||
int removeFrom = prevSegmentStart > offset ? prevSegmentStart : offset;
|
||||
uri.delete(removeFrom, nextSegmentStart);
|
||||
limit -= nextSegmentStart - removeFrom;
|
||||
segmentStart = prevSegmentStart;
|
||||
i = prevSegmentStart;
|
||||
} else {
|
||||
i++;
|
||||
segmentStart = i;
|
||||
}
|
||||
}
|
||||
return uri.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates indices of the constituent components of a URI.
|
||||
*
|
||||
* @param uriString The URI as a string.
|
||||
* @return The corresponding indices.
|
||||
*/
|
||||
private static int[] getUriIndices(String uriString) {
|
||||
int[] indices = new int[INDEX_COUNT];
|
||||
if (TextUtils.isEmpty(uriString)) {
|
||||
indices[SCHEME_COLON] = -1;
|
||||
return indices;
|
||||
}
|
||||
|
||||
// Determine outer structure from right to left.
|
||||
// Uri = scheme ":" hier-part [ "?" query ] [ "#" fragment ]
|
||||
int length = uriString.length();
|
||||
int fragmentIndex = uriString.indexOf('#');
|
||||
if (fragmentIndex == -1) {
|
||||
fragmentIndex = length;
|
||||
}
|
||||
int queryIndex = uriString.indexOf('?');
|
||||
if (queryIndex == -1 || queryIndex > fragmentIndex) {
|
||||
// '#' before '?': '?' is within the fragment.
|
||||
queryIndex = fragmentIndex;
|
||||
}
|
||||
// Slashes are allowed only in hier-part so any colon after the first slash is part of the
|
||||
// hier-part, not the scheme colon separator.
|
||||
int schemeIndexLimit = uriString.indexOf('/');
|
||||
if (schemeIndexLimit == -1 || schemeIndexLimit > queryIndex) {
|
||||
schemeIndexLimit = queryIndex;
|
||||
}
|
||||
int schemeIndex = uriString.indexOf(':');
|
||||
if (schemeIndex > schemeIndexLimit) {
|
||||
// '/' before ':'
|
||||
schemeIndex = -1;
|
||||
}
|
||||
|
||||
// Determine hier-part structure: hier-part = "//" authority path / path
|
||||
// This block can also cope with schemeIndex == -1.
|
||||
boolean hasAuthority = schemeIndex + 2 < queryIndex
|
||||
&& uriString.charAt(schemeIndex + 1) == '/'
|
||||
&& uriString.charAt(schemeIndex + 2) == '/';
|
||||
int pathIndex;
|
||||
if (hasAuthority) {
|
||||
pathIndex = uriString.indexOf('/', schemeIndex + 3); // find first '/' after "://"
|
||||
if (pathIndex == -1 || pathIndex > queryIndex) {
|
||||
pathIndex = queryIndex;
|
||||
}
|
||||
} else {
|
||||
pathIndex = schemeIndex + 1;
|
||||
}
|
||||
|
||||
indices[SCHEME_COLON] = schemeIndex;
|
||||
indices[PATH] = pathIndex;
|
||||
indices[QUERY] = queryIndex;
|
||||
indices[FRAGMENT] = fragmentIndex;
|
||||
return indices;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -17,7 +17,6 @@ package com.google.android.exoplayer.util;
|
|||
|
||||
import com.google.android.exoplayer.upstream.DataSource;
|
||||
|
||||
import android.net.Uri;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
|
|
@ -134,54 +133,6 @@ public final class Util {
|
|||
return text == null ? null : text.toLowerCase(Locale.US);
|
||||
}
|
||||
|
||||
/**
|
||||
* Like {@link Uri#parse(String)}, but discards the part of the uri that follows the final
|
||||
* forward slash.
|
||||
*
|
||||
* @param uriString An RFC 2396-compliant, encoded uri.
|
||||
* @return The parsed base uri.
|
||||
*/
|
||||
public static Uri parseBaseUri(String uriString) {
|
||||
return Uri.parse(uriString.substring(0, uriString.lastIndexOf('/')));
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges a uri and a string to produce a new uri.
|
||||
* <p>
|
||||
* The uri is built according to the following rules:
|
||||
* <ul>
|
||||
* <li>If {@code baseUri} is null or if {@code stringUri} is absolute, then {@code baseUri} is
|
||||
* ignored and the uri consists solely of {@code stringUri}.
|
||||
* <li>If {@code stringUri} is null, then the uri consists solely of {@code baseUrl}.
|
||||
* <li>Otherwise, the uri consists of the concatenation of {@code baseUri} and {@code stringUri}.
|
||||
* </ul>
|
||||
*
|
||||
* @param baseUri A uri that can form the base of the merged uri.
|
||||
* @param stringUri A relative or absolute uri in string form.
|
||||
* @return The merged uri.
|
||||
*/
|
||||
public static Uri getMergedUri(Uri baseUri, String stringUri) {
|
||||
if (stringUri == null) {
|
||||
return baseUri;
|
||||
}
|
||||
if (baseUri == null) {
|
||||
return Uri.parse(stringUri);
|
||||
}
|
||||
if (stringUri.startsWith("/")) {
|
||||
stringUri = stringUri.substring(1);
|
||||
return new Uri.Builder()
|
||||
.scheme(baseUri.getScheme())
|
||||
.authority(baseUri.getAuthority())
|
||||
.appendEncodedPath(stringUri)
|
||||
.build();
|
||||
}
|
||||
Uri uri = Uri.parse(stringUri);
|
||||
if (uri.isAbsolute()) {
|
||||
return uri;
|
||||
}
|
||||
return Uri.withAppendedPath(baseUri, stringUri);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the index of the largest value in an array that is less than (or optionally equal to)
|
||||
* a specified key.
|
||||
|
|
|
|||
Loading…
Reference in a new issue