Commit 462fea3e by Oliver Woodman

Correctly resolve Uris according to RFC3986.

Issue: #327
parent 457557b5
...@@ -581,7 +581,7 @@ public class DashChunkSource implements ChunkSource { ...@@ -581,7 +581,7 @@ public class DashChunkSource implements ChunkSource {
} }
if ((result & Extractor.RESULT_READ_INDEX) != 0) { if ((result & Extractor.RESULT_READ_INDEX) != 0) {
representationHolders.get(format.id).segmentIndex = 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; ...@@ -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.dash.mpd.RangedUri;
import com.google.android.exoplayer.util.Util; import com.google.android.exoplayer.util.Util;
import android.net.Uri;
/** /**
* An implementation of {@link DashSegmentIndex} that wraps a {@link SegmentIndex} parsed from a * An implementation of {@link DashSegmentIndex} that wraps a {@link SegmentIndex} parsed from a
* media stream. * media stream.
...@@ -28,16 +26,16 @@ import android.net.Uri; ...@@ -28,16 +26,16 @@ import android.net.Uri;
public class DashWrappingSegmentIndex implements DashSegmentIndex { public class DashWrappingSegmentIndex implements DashSegmentIndex {
private final SegmentIndex segmentIndex; private final SegmentIndex segmentIndex;
private final Uri uri; private final String uri;
private final long indexAnchor; private final long indexAnchor;
/** /**
* @param segmentIndex The {@link SegmentIndex} to wrap. * @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 * @param indexAnchor The index anchor point. This value is added to the byte offsets specified
* in the wrapped {@link SegmentIndex}. * 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.segmentIndex = segmentIndex;
this.uri = uri; this.uri = uri;
this.indexAnchor = indexAnchor; this.indexAnchor = indexAnchor;
......
...@@ -24,9 +24,9 @@ import com.google.android.exoplayer.dash.mpd.SegmentBase.SingleSegmentBase; ...@@ -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.upstream.NetworkLoadable;
import com.google.android.exoplayer.util.Assertions; import com.google.android.exoplayer.util.Assertions;
import com.google.android.exoplayer.util.MimeTypes; import com.google.android.exoplayer.util.MimeTypes;
import com.google.android.exoplayer.util.UriUtil;
import com.google.android.exoplayer.util.Util; import com.google.android.exoplayer.util.Util;
import android.net.Uri;
import android.text.TextUtils; import android.text.TextUtils;
import org.xml.sax.helpers.DefaultHandler; import org.xml.sax.helpers.DefaultHandler;
...@@ -83,7 +83,7 @@ public class MediaPresentationDescriptionParser extends DefaultHandler ...@@ -83,7 +83,7 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
throw new ParserException( throw new ParserException(
"inputStream does not contain a valid media presentation description"); "inputStream does not contain a valid media presentation description");
} }
return parseMediaPresentationDescription(xpp, Util.parseBaseUri(connectionUrl)); return parseMediaPresentationDescription(xpp, connectionUrl);
} catch (XmlPullParserException e) { } catch (XmlPullParserException e) {
throw new ParserException(e); throw new ParserException(e);
} catch (ParseException e) { } catch (ParseException e) {
...@@ -92,7 +92,7 @@ public class MediaPresentationDescriptionParser extends DefaultHandler ...@@ -92,7 +92,7 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
} }
protected MediaPresentationDescription parseMediaPresentationDescription(XmlPullParser xpp, protected MediaPresentationDescription parseMediaPresentationDescription(XmlPullParser xpp,
Uri baseUrl) throws XmlPullParserException, IOException, ParseException { String baseUrl) throws XmlPullParserException, IOException, ParseException {
long availabilityStartTime = parseDateTime(xpp, "availabilityStartTime", -1); long availabilityStartTime = parseDateTime(xpp, "availabilityStartTime", -1);
long durationMs = parseDuration(xpp, "mediaPresentationDuration", -1); long durationMs = parseDuration(xpp, "mediaPresentationDuration", -1);
long minBufferTimeMs = parseDuration(xpp, "minBufferTime", -1); long minBufferTimeMs = parseDuration(xpp, "minBufferTime", -1);
...@@ -137,7 +137,7 @@ public class MediaPresentationDescriptionParser extends DefaultHandler ...@@ -137,7 +137,7 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
return new UtcTimingElement(schemeIdUri, value); 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 { throws XmlPullParserException, IOException {
String id = xpp.getAttributeValue(null, "id"); String id = xpp.getAttributeValue(null, "id");
long startMs = parseDuration(xpp, "start", 0); long startMs = parseDuration(xpp, "start", 0);
...@@ -170,7 +170,7 @@ public class MediaPresentationDescriptionParser extends DefaultHandler ...@@ -170,7 +170,7 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
// AdaptationSet parsing. // 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 { long periodDurationMs, SegmentBase segmentBase) throws XmlPullParserException, IOException {
String mimeType = xpp.getAttributeValue(null, "mimeType"); String mimeType = xpp.getAttributeValue(null, "mimeType");
...@@ -287,9 +287,9 @@ public class MediaPresentationDescriptionParser extends DefaultHandler ...@@ -287,9 +287,9 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
// Representation parsing. // Representation parsing.
protected Representation parseRepresentation(XmlPullParser xpp, Uri baseUrl, long periodStartMs, protected Representation parseRepresentation(XmlPullParser xpp, String baseUrl,
long periodDurationMs, String mimeType, String language, SegmentBase segmentBase) long periodStartMs, long periodDurationMs, String mimeType, String language,
throws XmlPullParserException, IOException { SegmentBase segmentBase) throws XmlPullParserException, IOException {
String id = xpp.getAttributeValue(null, "id"); String id = xpp.getAttributeValue(null, "id");
int bandwidth = parseInt(xpp, "bandwidth"); int bandwidth = parseInt(xpp, "bandwidth");
int audioSamplingRate = parseInt(xpp, "audioSamplingRate"); int audioSamplingRate = parseInt(xpp, "audioSamplingRate");
...@@ -335,7 +335,7 @@ public class MediaPresentationDescriptionParser extends DefaultHandler ...@@ -335,7 +335,7 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
// SegmentBase, SegmentList and SegmentTemplate parsing. // SegmentBase, SegmentList and SegmentTemplate parsing.
protected SingleSegmentBase parseSegmentBase(XmlPullParser xpp, Uri baseUrl, protected SingleSegmentBase parseSegmentBase(XmlPullParser xpp, String baseUrl,
SingleSegmentBase parent) throws XmlPullParserException, IOException { SingleSegmentBase parent) throws XmlPullParserException, IOException {
long timescale = parseLong(xpp, "timescale", parent != null ? parent.timescale : 1); long timescale = parseLong(xpp, "timescale", parent != null ? parent.timescale : 1);
...@@ -364,12 +364,12 @@ public class MediaPresentationDescriptionParser extends DefaultHandler ...@@ -364,12 +364,12 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
} }
protected SingleSegmentBase buildSingleSegmentBase(RangedUri initialization, long timescale, 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, return new SingleSegmentBase(initialization, timescale, presentationTimeOffset, baseUrl,
indexStart, indexLength); 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 periodDurationMs) throws XmlPullParserException, IOException {
long timescale = parseLong(xpp, "timescale", parent != null ? parent.timescale : 1); long timescale = parseLong(xpp, "timescale", parent != null ? parent.timescale : 1);
...@@ -413,7 +413,7 @@ public class MediaPresentationDescriptionParser extends DefaultHandler ...@@ -413,7 +413,7 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
startNumber, duration, timeline, segments); 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 { SegmentTemplate parent, long periodDurationMs) throws XmlPullParserException, IOException {
long timescale = parseLong(xpp, "timescale", parent != null ? parent.timescale : 1); long timescale = parseLong(xpp, "timescale", parent != null ? parent.timescale : 1);
...@@ -450,7 +450,7 @@ public class MediaPresentationDescriptionParser extends DefaultHandler ...@@ -450,7 +450,7 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
protected SegmentTemplate buildSegmentTemplate(RangedUri initialization, long timescale, protected SegmentTemplate buildSegmentTemplate(RangedUri initialization, long timescale,
long presentationTimeOffset, long periodDurationMs, int startNumber, long duration, long presentationTimeOffset, long periodDurationMs, int startNumber, long duration,
List<SegmentTimelineElement> timeline, UrlTemplate initializationTemplate, List<SegmentTimelineElement> timeline, UrlTemplate initializationTemplate,
UrlTemplate mediaTemplate, Uri baseUrl) { UrlTemplate mediaTemplate, String baseUrl) {
return new SegmentTemplate(initialization, timescale, presentationTimeOffset, periodDurationMs, return new SegmentTemplate(initialization, timescale, presentationTimeOffset, periodDurationMs,
startNumber, duration, timeline, initializationTemplate, mediaTemplate, baseUrl); startNumber, duration, timeline, initializationTemplate, mediaTemplate, baseUrl);
} }
...@@ -487,15 +487,15 @@ public class MediaPresentationDescriptionParser extends DefaultHandler ...@@ -487,15 +487,15 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
return defaultValue; return defaultValue;
} }
protected RangedUri parseInitialization(XmlPullParser xpp, Uri baseUrl) { protected RangedUri parseInitialization(XmlPullParser xpp, String baseUrl) {
return parseRangedUrl(xpp, baseUrl, "sourceURL", "range"); 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"); 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 rangeAttribute) {
String urlText = xpp.getAttributeValue(null, urlAttribute); String urlText = xpp.getAttributeValue(null, urlAttribute);
long rangeStart = 0; long rangeStart = 0;
...@@ -509,7 +509,7 @@ public class MediaPresentationDescriptionParser extends DefaultHandler ...@@ -509,7 +509,7 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
return buildRangedUri(baseUrl, urlText, rangeStart, rangeLength); 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) { long rangeLength) {
return new RangedUri(baseUrl, urlText, rangeStart, rangeLength); return new RangedUri(baseUrl, urlText, rangeStart, rangeLength);
} }
...@@ -548,15 +548,10 @@ public class MediaPresentationDescriptionParser extends DefaultHandler ...@@ -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 { throws XmlPullParserException, IOException {
xpp.next(); xpp.next();
String newBaseUrlText = xpp.getText(); return UriUtil.resolve(parentBaseUrl, xpp.getText());
Uri newBaseUri = Uri.parse(newBaseUrlText);
if (!newBaseUri.isAbsolute()) {
newBaseUri = Uri.withAppendedPath(parentBaseUrl, newBaseUrlText);
}
return newBaseUri;
} }
protected static int parseInt(XmlPullParser xpp, String name) { protected static int parseInt(XmlPullParser xpp, String name) {
......
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
package com.google.android.exoplayer.dash.mpd; package com.google.android.exoplayer.dash.mpd;
import com.google.android.exoplayer.util.Assertions; 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; import android.net.Uri;
...@@ -35,31 +35,28 @@ public final class RangedUri { ...@@ -35,31 +35,28 @@ public final class RangedUri {
*/ */
public final long length; public final long length;
// The {@link Uri} is stored internally in two parts, {@link #baseUri} and {@link uriString}. // The URI is stored internally in two parts: reference URI and a base URI to use when
// This helps optimize memory usage in the same way that DASH manifests allow many URLs to be // resolving it. This helps optimize memory usage in the same way that DASH manifests allow many
// expressed concisely in the form of a single BaseURL and many relative paths. Note that this // URLs to be expressed concisely in the form of a single BaseURL and many relative paths. Note
// optimization relies on the same {@code Uri} being passed as the {@link #baseUri} to many // that this optimization relies on the same object being passed as the base URI to many
// instances of this class. // instances of this class.
private final Uri baseUri; private final String baseUri;
private final String stringUri; private final String referenceUri;
private int hashCode; private int hashCode;
/** /**
* Constructs an ranged uri. * 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 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 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. * @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) { public RangedUri(String baseUri, String referenceUri, long start, long length) {
Assertions.checkArgument(baseUri != null || stringUri != null); Assertions.checkArgument(baseUri != null || referenceUri != null);
this.baseUri = baseUri; this.baseUri = baseUri;
this.stringUri = stringUri; this.referenceUri = referenceUri;
this.start = start; this.start = start;
this.length = length; this.length = length;
} }
...@@ -70,7 +67,16 @@ public final class RangedUri { ...@@ -70,7 +67,16 @@ public final class RangedUri {
* @return The {@link Uri} represented by the instance. * @return The {@link Uri} represented by the instance.
*/ */
public Uri getUri() { 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 { ...@@ -85,13 +91,13 @@ public final class RangedUri {
* @return The merged {@link RangedUri} if the merge was successful. Null otherwise. * @return The merged {@link RangedUri} if the merge was successful. Null otherwise.
*/ */
public RangedUri attemptMerge(RangedUri other) { public RangedUri attemptMerge(RangedUri other) {
if (other == null || !getUri().equals(other.getUri())) { if (other == null || !getUriString().equals(other.getUriString())) {
return null; return null;
} else if (length != -1 && start + length == other.start) { } 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); other.length == -1 ? -1 : length + other.length);
} else if (other.length != -1 && other.start + other.length == start) { } 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); length == -1 ? -1 : other.length + length);
} else { } else {
return null; return null;
...@@ -104,7 +110,7 @@ public final class RangedUri { ...@@ -104,7 +110,7 @@ public final class RangedUri {
int result = 17; int result = 17;
result = 31 * result + (int) start; result = 31 * result + (int) start;
result = 31 * result + (int) length; result = 31 * result + (int) length;
result = 31 * result + getUri().hashCode(); result = 31 * result + getUriString().hashCode();
hashCode = result; hashCode = result;
} }
return hashCode; return hashCode;
...@@ -121,7 +127,7 @@ public final class RangedUri { ...@@ -121,7 +127,7 @@ public final class RangedUri {
RangedUri other = (RangedUri) obj; RangedUri other = (RangedUri) obj;
return this.start == other.start return this.start == other.start
&& this.length == other.length && this.length == other.length
&& getUri().equals(other.getUri()); && getUriString().equals(other.getUriString());
} }
} }
...@@ -147,7 +147,7 @@ public abstract class Representation { ...@@ -147,7 +147,7 @@ public abstract class Representation {
public static class SingleSegmentRepresentation extends Representation { public static class SingleSegmentRepresentation extends Representation {
/** /**
* The {@link Uri} of the single segment. * The uri of the single segment.
*/ */
public final Uri uri; public final Uri uri;
...@@ -174,7 +174,7 @@ public abstract class Representation { ...@@ -174,7 +174,7 @@ public abstract class Representation {
* @param contentLength The content length, or -1 if unknown. * @param contentLength The content length, or -1 if unknown.
*/ */
public static SingleSegmentRepresentation newInstance(long periodStartMs, long periodDurationMs, 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) { long initializationEnd, long indexStart, long indexEnd, long contentLength) {
RangedUri rangedUri = new RangedUri(uri, null, initializationStart, RangedUri rangedUri = new RangedUri(uri, null, initializationStart,
initializationEnd - initializationStart + 1); initializationEnd - initializationStart + 1);
...@@ -197,13 +197,13 @@ public abstract class Representation { ...@@ -197,13 +197,13 @@ public abstract class Representation {
public SingleSegmentRepresentation(long periodStartMs, long periodDurationMs, String contentId, public SingleSegmentRepresentation(long periodStartMs, long periodDurationMs, String contentId,
long revisionId, Format format, SingleSegmentBase segmentBase, long contentLength) { long revisionId, Format format, SingleSegmentBase segmentBase, long contentLength) {
super(periodStartMs, periodDurationMs, contentId, revisionId, format, segmentBase); super(periodStartMs, periodDurationMs, contentId, revisionId, format, segmentBase);
this.uri = segmentBase.uri; this.uri = Uri.parse(segmentBase.uri);
this.indexUri = segmentBase.getIndex(); this.indexUri = segmentBase.getIndex();
this.contentLength = contentLength; this.contentLength = contentLength;
// If we have an index uri then the index is defined externally, and we shouldn't return one // 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. // 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, 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 @Override
......
...@@ -19,8 +19,6 @@ import com.google.android.exoplayer.C; ...@@ -19,8 +19,6 @@ import com.google.android.exoplayer.C;
import com.google.android.exoplayer.dash.DashSegmentIndex; import com.google.android.exoplayer.dash.DashSegmentIndex;
import com.google.android.exoplayer.util.Util; import com.google.android.exoplayer.util.Util;
import android.net.Uri;
import java.util.List; import java.util.List;
/** /**
...@@ -73,7 +71,7 @@ public abstract class SegmentBase { ...@@ -73,7 +71,7 @@ public abstract class SegmentBase {
/** /**
* The uri of the segment. * The uri of the segment.
*/ */
public final Uri uri; public final String uri;
/* package */ final long indexStart; /* package */ final long indexStart;
/* package */ final long indexLength; /* package */ final long indexLength;
...@@ -89,7 +87,7 @@ public abstract class SegmentBase { ...@@ -89,7 +87,7 @@ public abstract class SegmentBase {
* @param indexLength The length of the index data in bytes. * @param indexLength The length of the index data in bytes.
*/ */
public SingleSegmentBase(RangedUri initialization, long timescale, long presentationTimeOffset, public SingleSegmentBase(RangedUri initialization, long timescale, long presentationTimeOffset,
Uri uri, long indexStart, long indexLength) { String uri, long indexStart, long indexLength) {
super(initialization, timescale, presentationTimeOffset); super(initialization, timescale, presentationTimeOffset);
this.uri = uri; this.uri = uri;
this.indexStart = indexStart; this.indexStart = indexStart;
...@@ -99,7 +97,7 @@ public abstract class SegmentBase { ...@@ -99,7 +97,7 @@ public abstract class SegmentBase {
/** /**
* @param uri The uri of the segment. * @param uri The uri of the segment.
*/ */
public SingleSegmentBase(Uri uri) { public SingleSegmentBase(String uri) {
this(null, 1, 0, uri, 0, -1); this(null, 1, 0, uri, 0, -1);
} }
...@@ -289,7 +287,7 @@ public abstract class SegmentBase { ...@@ -289,7 +287,7 @@ public abstract class SegmentBase {
/* package */ final UrlTemplate initializationTemplate; /* package */ final UrlTemplate initializationTemplate;
/* package */ final UrlTemplate mediaTemplate; /* package */ final UrlTemplate mediaTemplate;
private final Uri baseUrl; private final String baseUrl;
/** /**
* @param initialization A {@link RangedUri} corresponding to initialization data, if such data * @param initialization A {@link RangedUri} corresponding to initialization data, if such data
...@@ -315,7 +313,7 @@ public abstract class SegmentBase { ...@@ -315,7 +313,7 @@ public abstract class SegmentBase {
public SegmentTemplate(RangedUri initialization, long timescale, long presentationTimeOffset, public SegmentTemplate(RangedUri initialization, long timescale, long presentationTimeOffset,
long periodDurationMs, int startNumber, long duration, long periodDurationMs, int startNumber, long duration,
List<SegmentTimelineElement> segmentTimeline, UrlTemplate initializationTemplate, List<SegmentTimelineElement> segmentTimeline, UrlTemplate initializationTemplate,
UrlTemplate mediaTemplate, Uri baseUrl) { UrlTemplate mediaTemplate, String baseUrl) {
super(initialization, timescale, presentationTimeOffset, periodDurationMs, startNumber, super(initialization, timescale, presentationTimeOffset, periodDurationMs, startNumber,
duration, segmentTimeline); duration, segmentTimeline);
this.initializationTemplate = initializationTemplate; this.initializationTemplate = initializationTemplate;
......
...@@ -27,6 +27,7 @@ import com.google.android.exoplayer.upstream.DataSource; ...@@ -27,6 +27,7 @@ import com.google.android.exoplayer.upstream.DataSource;
import com.google.android.exoplayer.upstream.DataSpec; import com.google.android.exoplayer.upstream.DataSpec;
import com.google.android.exoplayer.upstream.HttpDataSource.InvalidResponseCodeException; import com.google.android.exoplayer.upstream.HttpDataSource.InvalidResponseCodeException;
import com.google.android.exoplayer.util.Assertions; import com.google.android.exoplayer.util.Assertions;
import com.google.android.exoplayer.util.UriUtil;
import com.google.android.exoplayer.util.Util; import com.google.android.exoplayer.util.Util;
import android.net.Uri; import android.net.Uri;
...@@ -121,7 +122,7 @@ public class HlsChunkSource { ...@@ -121,7 +122,7 @@ public class HlsChunkSource {
private final Variant[] enabledVariants; private final Variant[] enabledVariants;
private final BandwidthMeter bandwidthMeter; private final BandwidthMeter bandwidthMeter;
private final int adaptiveMode; private final int adaptiveMode;
private final Uri baseUri; private final String baseUri;
private final int maxWidth; private final int maxWidth;
private final int maxHeight; private final int maxHeight;
private final int targetBufferSize; private final int targetBufferSize;
...@@ -301,11 +302,11 @@ public class HlsChunkSource { ...@@ -301,11 +302,11 @@ public class HlsChunkSource {
} }
HlsMediaPlaylist.Segment segment = mediaPlaylist.segments.get(chunkIndex); 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. // Check if encryption is specified.
if (HlsMediaPlaylist.ENCRYPTION_METHOD_AES_128.equals(segment.encryptionMethod)) { 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)) { if (!keyUri.equals(encryptionKeyUri)) {
// Encryption is specified and the key has changed. // Encryption is specified and the key has changed.
HlsChunk toReturn = newEncryptionKeyChunk(keyUri, segment.encryptionIV); HlsChunk toReturn = newEncryptionKeyChunk(keyUri, segment.encryptionIV);
...@@ -437,7 +438,7 @@ public class HlsChunkSource { ...@@ -437,7 +438,7 @@ public class HlsChunkSource {
} }
private MediaPlaylistChunk newMediaPlaylistChunk(int variantIndex) { 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 dataSpec = new DataSpec(mediaPlaylistUri, 0, C.LENGTH_UNBOUNDED, null,
DataSpec.FLAG_ALLOW_GZIP); DataSpec.FLAG_ALLOW_GZIP);
return new MediaPlaylistChunk(variantIndex, upstreamDataSource, dataSpec, return new MediaPlaylistChunk(variantIndex, upstreamDataSource, dataSpec,
......
...@@ -15,8 +15,6 @@ ...@@ -15,8 +15,6 @@
*/ */
package com.google.android.exoplayer.hls; package com.google.android.exoplayer.hls;
import android.net.Uri;
import java.util.List; import java.util.List;
/** /**
...@@ -26,7 +24,7 @@ public final class HlsMasterPlaylist extends HlsPlaylist { ...@@ -26,7 +24,7 @@ public final class HlsMasterPlaylist extends HlsPlaylist {
public final List<Variant> variants; public final List<Variant> variants;
public HlsMasterPlaylist(Uri baseUri, List<Variant> variants) { public HlsMasterPlaylist(String baseUri, List<Variant> variants) {
super(baseUri, HlsPlaylist.TYPE_MASTER); super(baseUri, HlsPlaylist.TYPE_MASTER);
this.variants = variants; this.variants = variants;
} }
......
...@@ -17,8 +17,6 @@ package com.google.android.exoplayer.hls; ...@@ -17,8 +17,6 @@ package com.google.android.exoplayer.hls;
import com.google.android.exoplayer.C; import com.google.android.exoplayer.C;
import android.net.Uri;
import java.util.List; import java.util.List;
/** /**
...@@ -70,7 +68,7 @@ public final class HlsMediaPlaylist extends HlsPlaylist { ...@@ -70,7 +68,7 @@ public final class HlsMediaPlaylist extends HlsPlaylist {
public final boolean live; public final boolean live;
public final long durationUs; 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) { boolean live, List<Segment> segments) {
super(baseUri, HlsPlaylist.TYPE_MEDIA); super(baseUri, HlsPlaylist.TYPE_MEDIA);
this.mediaSequence = mediaSequence; this.mediaSequence = mediaSequence;
......
...@@ -15,7 +15,6 @@ ...@@ -15,7 +15,6 @@
*/ */
package com.google.android.exoplayer.hls; package com.google.android.exoplayer.hls;
import android.net.Uri;
/** /**
* Represents an HLS playlist. * Represents an HLS playlist.
...@@ -25,10 +24,10 @@ public abstract class HlsPlaylist { ...@@ -25,10 +24,10 @@ public abstract class HlsPlaylist {
public final static int TYPE_MASTER = 0; public final static int TYPE_MASTER = 0;
public final static int TYPE_MEDIA = 1; public final static int TYPE_MEDIA = 1;
public final Uri baseUri; public final String baseUri;
public final int type; public final int type;
protected HlsPlaylist(Uri baseUri, int type) { protected HlsPlaylist(String baseUri, int type) {
this.baseUri = baseUri; this.baseUri = baseUri;
this.type = type; this.type = type;
} }
......
...@@ -19,9 +19,6 @@ import com.google.android.exoplayer.C; ...@@ -19,9 +19,6 @@ import com.google.android.exoplayer.C;
import com.google.android.exoplayer.ParserException; import com.google.android.exoplayer.ParserException;
import com.google.android.exoplayer.hls.HlsMediaPlaylist.Segment; import com.google.android.exoplayer.hls.HlsMediaPlaylist.Segment;
import com.google.android.exoplayer.upstream.NetworkLoadable; 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.BufferedReader;
import java.io.IOException; import java.io.IOException;
...@@ -86,7 +83,6 @@ public final class HlsPlaylistParser implements NetworkLoadable.Parser<HlsPlayli ...@@ -86,7 +83,6 @@ public final class HlsPlaylistParser implements NetworkLoadable.Parser<HlsPlayli
@Override @Override
public HlsPlaylist parse(String connectionUrl, InputStream inputStream) public HlsPlaylist parse(String connectionUrl, InputStream inputStream)
throws IOException, ParserException { throws IOException, ParserException {
Uri baseUri = Util.parseBaseUri(connectionUrl);
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
Queue<String> extraLines = new LinkedList<String>(); Queue<String> extraLines = new LinkedList<String>();
String line; String line;
...@@ -97,7 +93,7 @@ public final class HlsPlaylistParser implements NetworkLoadable.Parser<HlsPlayli ...@@ -97,7 +93,7 @@ public final class HlsPlaylistParser implements NetworkLoadable.Parser<HlsPlayli
// Do nothing. // Do nothing.
} else if (line.startsWith(STREAM_INF_TAG)) { } else if (line.startsWith(STREAM_INF_TAG)) {
extraLines.add(line); extraLines.add(line);
return parseMasterPlaylist(new LineIterator(extraLines, reader), baseUri); return parseMasterPlaylist(new LineIterator(extraLines, reader), connectionUrl);
} else if (line.startsWith(TARGET_DURATION_TAG) } else if (line.startsWith(TARGET_DURATION_TAG)
|| line.startsWith(MEDIA_SEQUENCE_TAG) || line.startsWith(MEDIA_SEQUENCE_TAG)
|| line.startsWith(MEDIA_DURATION_TAG) || line.startsWith(MEDIA_DURATION_TAG)
...@@ -106,7 +102,7 @@ public final class HlsPlaylistParser implements NetworkLoadable.Parser<HlsPlayli ...@@ -106,7 +102,7 @@ public final class HlsPlaylistParser implements NetworkLoadable.Parser<HlsPlayli
|| line.equals(DISCONTINUITY_TAG) || line.equals(DISCONTINUITY_TAG)
|| line.equals(ENDLIST_TAG)) { || line.equals(ENDLIST_TAG)) {
extraLines.add(line); extraLines.add(line);
return parseMediaPlaylist(new LineIterator(extraLines, reader), baseUri); return parseMediaPlaylist(new LineIterator(extraLines, reader), connectionUrl);
} else if (line.startsWith(VERSION_TAG)) { } else if (line.startsWith(VERSION_TAG)) {
extraLines.add(line); extraLines.add(line);
} else if (!line.startsWith("#")) { } else if (!line.startsWith("#")) {
...@@ -119,7 +115,7 @@ public final class HlsPlaylistParser implements NetworkLoadable.Parser<HlsPlayli ...@@ -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."); 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 { throws IOException {
List<Variant> variants = new ArrayList<Variant>(); List<Variant> variants = new ArrayList<Variant>();
int bandwidth = 0; int bandwidth = 0;
...@@ -160,7 +156,7 @@ public final class HlsPlaylistParser implements NetworkLoadable.Parser<HlsPlayli ...@@ -160,7 +156,7 @@ public final class HlsPlaylistParser implements NetworkLoadable.Parser<HlsPlayli
return new HlsMasterPlaylist(baseUri, Collections.unmodifiableList(variants)); 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 { throws IOException {
int mediaSequence = 0; int mediaSequence = 0;
int targetDurationSecs = 0; int targetDurationSecs = 0;
......
...@@ -17,6 +17,7 @@ package com.google.android.exoplayer.smoothstreaming; ...@@ -17,6 +17,7 @@ package com.google.android.exoplayer.smoothstreaming;
import com.google.android.exoplayer.C; import com.google.android.exoplayer.C;
import com.google.android.exoplayer.util.Assertions; import com.google.android.exoplayer.util.Assertions;
import com.google.android.exoplayer.util.UriUtil;
import com.google.android.exoplayer.util.Util; import com.google.android.exoplayer.util.Util;
import android.net.Uri; import android.net.Uri;
...@@ -197,14 +198,14 @@ public class SmoothStreamingManifest { ...@@ -197,14 +198,14 @@ public class SmoothStreamingManifest {
public final TrackElement[] tracks; public final TrackElement[] tracks;
public final int chunkCount; public final int chunkCount;
private final Uri baseUri; private final String baseUri;
private final String chunkTemplate; private final String chunkTemplate;
private final List<Long> chunkStartTimes; private final List<Long> chunkStartTimes;
private final long[] chunkStartTimesUs; private final long[] chunkStartTimesUs;
private final long lastChunkDurationUs; 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, long timescale, String name, int qualityLevels, int maxWidth, int maxHeight,
int displayWidth, int displayHeight, String language, TrackElement[] tracks, int displayWidth, int displayHeight, String language, TrackElement[] tracks,
List<Long> chunkStartTimes, long lastChunkDuration) { List<Long> chunkStartTimes, long lastChunkDuration) {
...@@ -274,7 +275,7 @@ public class SmoothStreamingManifest { ...@@ -274,7 +275,7 @@ public class SmoothStreamingManifest {
String chunkUrl = chunkTemplate String chunkUrl = chunkTemplate
.replace(URL_PLACEHOLDER_BITRATE, Integer.toString(tracks[track].bitrate)) .replace(URL_PLACEHOLDER_BITRATE, Integer.toString(tracks[track].bitrate))
.replace(URL_PLACEHOLDER_START_TIME, chunkStartTimes.get(chunkIndex).toString()); .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; ...@@ -23,9 +23,7 @@ import com.google.android.exoplayer.upstream.NetworkLoadable;
import com.google.android.exoplayer.util.Assertions; import com.google.android.exoplayer.util.Assertions;
import com.google.android.exoplayer.util.CodecSpecificDataUtil; import com.google.android.exoplayer.util.CodecSpecificDataUtil;
import com.google.android.exoplayer.util.MimeTypes; 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.Base64;
import android.util.Pair; import android.util.Pair;
...@@ -65,8 +63,8 @@ public class SmoothStreamingManifestParser implements ...@@ -65,8 +63,8 @@ public class SmoothStreamingManifestParser implements
try { try {
XmlPullParser xmlParser = xmlParserFactory.newPullParser(); XmlPullParser xmlParser = xmlParserFactory.newPullParser();
xmlParser.setInput(inputStream, null); xmlParser.setInput(inputStream, null);
SmoothStreamMediaParser smoothStreamMediaParser = new SmoothStreamMediaParser(null, SmoothStreamMediaParser smoothStreamMediaParser =
Util.parseBaseUri(connectionUrl)); new SmoothStreamMediaParser(null, connectionUrl);
return (SmoothStreamingManifest) smoothStreamMediaParser.parse(xmlParser); return (SmoothStreamingManifest) smoothStreamMediaParser.parse(xmlParser);
} catch (XmlPullParserException e) { } catch (XmlPullParserException e) {
throw new ParserException(e); throw new ParserException(e);
...@@ -89,13 +87,13 @@ public class SmoothStreamingManifestParser implements ...@@ -89,13 +87,13 @@ public class SmoothStreamingManifestParser implements
*/ */
private static abstract class ElementParser { private static abstract class ElementParser {
private final Uri baseUri; private final String baseUri;
private final String tag; private final String tag;
private final ElementParser parent; private final ElementParser parent;
private final List<Pair<String, Object>> normalizedAttributes; 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.parent = parent;
this.baseUri = baseUri; this.baseUri = baseUri;
this.tag = tag; this.tag = tag;
...@@ -158,7 +156,7 @@ public class SmoothStreamingManifestParser implements ...@@ -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)) { if (TrackElementParser.TAG.equals(name)) {
return new TrackElementParser(parent, baseUri); return new TrackElementParser(parent, baseUri);
} else if (ProtectionElementParser.TAG.equals(name)) { } else if (ProtectionElementParser.TAG.equals(name)) {
...@@ -342,7 +340,7 @@ public class SmoothStreamingManifestParser implements ...@@ -342,7 +340,7 @@ public class SmoothStreamingManifestParser implements
private ProtectionElement protectionElement; private ProtectionElement protectionElement;
private List<StreamElement> streamElements; private List<StreamElement> streamElements;
public SmoothStreamMediaParser(ElementParser parent, Uri baseUri) { public SmoothStreamMediaParser(ElementParser parent, String baseUri) {
super(parent, baseUri, TAG); super(parent, baseUri, TAG);
lookAheadCount = -1; lookAheadCount = -1;
protectionElement = null; protectionElement = null;
...@@ -392,7 +390,7 @@ public class SmoothStreamingManifestParser implements ...@@ -392,7 +390,7 @@ public class SmoothStreamingManifestParser implements
private UUID uuid; private UUID uuid;
private byte[] initData; private byte[] initData;
public ProtectionElementParser(ElementParser parent, Uri baseUri) { public ProtectionElementParser(ElementParser parent, String baseUri) {
super(parent, baseUri, TAG); super(parent, baseUri, TAG);
} }
...@@ -455,7 +453,7 @@ public class SmoothStreamingManifestParser implements ...@@ -455,7 +453,7 @@ public class SmoothStreamingManifestParser implements
private static final String KEY_FRAGMENT_START_TIME = "t"; private static final String KEY_FRAGMENT_START_TIME = "t";
private static final String KEY_FRAGMENT_REPEAT_COUNT = "r"; private static final String KEY_FRAGMENT_REPEAT_COUNT = "r";
private final Uri baseUri; private final String baseUri;
private final List<TrackElement> tracks; private final List<TrackElement> tracks;
private int type; private int type;
...@@ -473,7 +471,7 @@ public class SmoothStreamingManifestParser implements ...@@ -473,7 +471,7 @@ public class SmoothStreamingManifestParser implements
private long lastChunkDuration; private long lastChunkDuration;
public StreamElementParser(ElementParser parent, Uri baseUri) { public StreamElementParser(ElementParser parent, String baseUri) {
super(parent, baseUri, TAG); super(parent, baseUri, TAG);
this.baseUri = baseUri; this.baseUri = baseUri;
tracks = new LinkedList<TrackElement>(); tracks = new LinkedList<TrackElement>();
...@@ -615,7 +613,7 @@ public class SmoothStreamingManifestParser implements ...@@ -615,7 +613,7 @@ public class SmoothStreamingManifestParser implements
private int nalUnitLengthField; private int nalUnitLengthField;
private String content; private String content;
public TrackElementParser(ElementParser parent, Uri baseUri) { public TrackElementParser(ElementParser parent, String baseUri) {
super(parent, baseUri, TAG); super(parent, baseUri, TAG);
this.csd = new LinkedList<byte[]>(); this.csd = new LinkedList<byte[]>();
} }
......
...@@ -17,7 +17,6 @@ package com.google.android.exoplayer.util; ...@@ -17,7 +17,6 @@ package com.google.android.exoplayer.util;
import com.google.android.exoplayer.upstream.DataSource; import com.google.android.exoplayer.upstream.DataSource;
import android.net.Uri;
import android.text.TextUtils; import android.text.TextUtils;
import java.io.IOException; import java.io.IOException;
...@@ -135,54 +134,6 @@ public final class Util { ...@@ -135,54 +134,6 @@ public final class Util {
} }
/** /**
* 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) * Returns the index of the largest value in an array that is less than (or optionally equal to)
* a specified key. * a specified key.
* <p> * <p>
......
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