mirror of
https://github.com/samsonjs/media.git
synced 2026-04-27 15:07:40 +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) {
|
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;
|
||||||
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;
|
||||||
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;
|
||||||
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
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
||||||
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
|
||||||
|
|
||||||
// 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
|
||||||
|
|
||||||
// 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
|
||||||
|
|
||||||
// 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
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
||||||
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
|
||||||
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
|
||||||
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
|
||||||
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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 @@
|
||||||
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 {
|
||||||
*/
|
*/
|
||||||
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 {
|
||||||
* @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 {
|
||||||
* @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 {
|
||||||
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 {
|
||||||
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 {
|
||||||
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 {
|
||||||
* @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 {
|
||||||
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;
|
||||||
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 {
|
||||||
/**
|
/**
|
||||||
* 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 {
|
||||||
* @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 {
|
||||||
/**
|
/**
|
||||||
* @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 {
|
||||||
/* 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 {
|
||||||
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;
|
||||||
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 {
|
||||||
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 {
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
||||||
}
|
}
|
||||||
|
|
||||||
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 @@
|
||||||
*/
|
*/
|
||||||
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 {
|
||||||
|
|
||||||
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;
|
||||||
|
|
||||||
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 {
|
||||||
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 @@
|
||||||
*/
|
*/
|
||||||
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 {
|
||||||
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;
|
||||||
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
|
||||||
@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
|
||||||
// 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
|
||||||
|| 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
|
||||||
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
|
||||||
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;
|
||||||
|
|
||||||
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 {
|
||||||
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 {
|
||||||
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;
|
||||||
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
|
||||||
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
|
||||||
*/
|
*/
|
||||||
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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
||||||
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
|
||||||
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
|
||||||
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
|
||||||
|
|
||||||
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
|
||||||
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[]>();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 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;
|
||||||
|
|
@ -134,54 +133,6 @@ public final class Util {
|
||||||
return text == null ? null : text.toLowerCase(Locale.US);
|
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)
|
* Returns the index of the largest value in an array that is less than (or optionally equal to)
|
||||||
* a specified key.
|
* a specified key.
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue