parent
1b957268a6
commit
9e16dec2f8
@ -18,6 +18,8 @@ package com.google.android.exoplayer.dash.mpd;
|
|||||||
import com.google.android.exoplayer.ParserException;
|
import com.google.android.exoplayer.ParserException;
|
||||||
import com.google.android.exoplayer.util.ManifestFetcher;
|
import com.google.android.exoplayer.util.ManifestFetcher;
|
||||||
|
|
||||||
|
import android.net.Uri;
|
||||||
|
|
||||||
import org.xmlpull.v1.XmlPullParserException;
|
import org.xmlpull.v1.XmlPullParserException;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -57,9 +59,9 @@ public final class MediaPresentationDescriptionFetcher extends
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected MediaPresentationDescription parse(InputStream stream, String inputEncoding,
|
protected MediaPresentationDescription parse(InputStream stream, String inputEncoding,
|
||||||
String contentId) throws IOException, ParserException {
|
String contentId, Uri baseUrl) throws IOException, ParserException {
|
||||||
try {
|
try {
|
||||||
return parser.parseMediaPresentationDescription(stream, inputEncoding, contentId);
|
return parser.parseMediaPresentationDescription(stream, inputEncoding, contentId, baseUrl);
|
||||||
} catch (XmlPullParserException e) {
|
} catch (XmlPullParserException e) {
|
||||||
throw new ParserException(e);
|
throw new ParserException(e);
|
||||||
}
|
}
|
||||||
|
@ -64,14 +64,15 @@ public class MediaPresentationDescriptionParser extends DefaultHandler {
|
|||||||
* @param inputStream The stream from which to parse the manifest.
|
* @param inputStream The stream from which to parse the manifest.
|
||||||
* @param inputEncoding The encoding of the input.
|
* @param inputEncoding The encoding of the input.
|
||||||
* @param contentId The content id of the media.
|
* @param contentId The content id of the media.
|
||||||
|
* @param baseUrl The url that any relative urls defined within the manifest are relative to.
|
||||||
* @return The parsed manifest.
|
* @return The parsed manifest.
|
||||||
* @throws IOException If a problem occurred reading from the stream.
|
* @throws IOException If a problem occurred reading from the stream.
|
||||||
* @throws XmlPullParserException If a problem occurred parsing the stream as xml.
|
* @throws XmlPullParserException If a problem occurred parsing the stream as xml.
|
||||||
* @throws ParserException If a problem occurred parsing the xml as a DASH mpd.
|
* @throws ParserException If a problem occurred parsing the xml as a DASH mpd.
|
||||||
*/
|
*/
|
||||||
public MediaPresentationDescription parseMediaPresentationDescription(InputStream inputStream,
|
public MediaPresentationDescription parseMediaPresentationDescription(InputStream inputStream,
|
||||||
String inputEncoding, String contentId) throws XmlPullParserException, IOException,
|
String inputEncoding, String contentId, Uri baseUrl) throws XmlPullParserException,
|
||||||
ParserException {
|
IOException, ParserException {
|
||||||
XmlPullParser xpp = xmlParserFactory.newPullParser();
|
XmlPullParser xpp = xmlParserFactory.newPullParser();
|
||||||
xpp.setInput(inputStream, inputEncoding);
|
xpp.setInput(inputStream, inputEncoding);
|
||||||
int eventType = xpp.next();
|
int eventType = xpp.next();
|
||||||
@ -79,11 +80,12 @@ 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, contentId);
|
return parseMediaPresentationDescription(xpp, contentId, baseUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
private MediaPresentationDescription parseMediaPresentationDescription(XmlPullParser xpp,
|
private MediaPresentationDescription parseMediaPresentationDescription(XmlPullParser xpp,
|
||||||
String contentId) throws XmlPullParserException, IOException {
|
String contentId, Uri parentBaseUrl) throws XmlPullParserException, IOException {
|
||||||
|
Uri baseUrl = parentBaseUrl;
|
||||||
long duration = parseDurationMs(xpp, "mediaPresentationDuration");
|
long duration = parseDurationMs(xpp, "mediaPresentationDuration");
|
||||||
long minBufferTime = parseDurationMs(xpp, "minBufferTime");
|
long minBufferTime = parseDurationMs(xpp, "minBufferTime");
|
||||||
String typeString = xpp.getAttributeValue(null, "type");
|
String typeString = xpp.getAttributeValue(null, "type");
|
||||||
@ -93,8 +95,10 @@ public class MediaPresentationDescriptionParser extends DefaultHandler {
|
|||||||
List<Period> periods = new ArrayList<Period>();
|
List<Period> periods = new ArrayList<Period>();
|
||||||
do {
|
do {
|
||||||
xpp.next();
|
xpp.next();
|
||||||
if (isStartTag(xpp, "Period")) {
|
if (isStartTag(xpp, "BaseURL")) {
|
||||||
periods.add(parsePeriod(xpp, contentId, duration));
|
baseUrl = parseBaseUrl(xpp, parentBaseUrl);
|
||||||
|
} else if (isStartTag(xpp, "Period")) {
|
||||||
|
periods.add(parsePeriod(xpp, contentId, baseUrl, duration));
|
||||||
}
|
}
|
||||||
} while (!isEndTag(xpp, "MPD"));
|
} while (!isEndTag(xpp, "MPD"));
|
||||||
|
|
||||||
@ -102,8 +106,9 @@ public class MediaPresentationDescriptionParser extends DefaultHandler {
|
|||||||
periods);
|
periods);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Period parsePeriod(XmlPullParser xpp, String contentId, long mediaPresentationDuration)
|
private Period parsePeriod(XmlPullParser xpp, String contentId, Uri parentBaseUrl,
|
||||||
throws XmlPullParserException, IOException {
|
long mediaPresentationDuration) throws XmlPullParserException, IOException {
|
||||||
|
Uri baseUrl = parentBaseUrl;
|
||||||
int id = parseInt(xpp, "id");
|
int id = parseInt(xpp, "id");
|
||||||
long start = parseDurationMs(xpp, "start", 0);
|
long start = parseDurationMs(xpp, "start", 0);
|
||||||
long duration = parseDurationMs(xpp, "duration", mediaPresentationDuration);
|
long duration = parseDurationMs(xpp, "duration", mediaPresentationDuration);
|
||||||
@ -115,8 +120,10 @@ public class MediaPresentationDescriptionParser extends DefaultHandler {
|
|||||||
long presentationTimeOffset = 0;
|
long presentationTimeOffset = 0;
|
||||||
do {
|
do {
|
||||||
xpp.next();
|
xpp.next();
|
||||||
if (isStartTag(xpp, "AdaptationSet")) {
|
if (isStartTag(xpp, "BaseURL")) {
|
||||||
adaptationSets.add(parseAdaptationSet(xpp, contentId, start, duration,
|
baseUrl = parseBaseUrl(xpp, parentBaseUrl);
|
||||||
|
} else if (isStartTag(xpp, "AdaptationSet")) {
|
||||||
|
adaptationSets.add(parseAdaptationSet(xpp, contentId, baseUrl, start, duration,
|
||||||
segmentTimelineList));
|
segmentTimelineList));
|
||||||
} else if (isStartTag(xpp, "SegmentList")) {
|
} else if (isStartTag(xpp, "SegmentList")) {
|
||||||
segmentStartNumber = parseInt(xpp, "startNumber");
|
segmentStartNumber = parseInt(xpp, "startNumber");
|
||||||
@ -151,9 +158,10 @@ public class MediaPresentationDescriptionParser extends DefaultHandler {
|
|||||||
return segmentTimelineList;
|
return segmentTimelineList;
|
||||||
}
|
}
|
||||||
|
|
||||||
private AdaptationSet parseAdaptationSet(XmlPullParser xpp, String contentId, long periodStart,
|
private AdaptationSet parseAdaptationSet(XmlPullParser xpp, String contentId, Uri parentBaseUrl,
|
||||||
long periodDuration, List<Segment.Timeline> segmentTimelineList)
|
long periodStart, long periodDuration, List<Segment.Timeline> segmentTimelineList)
|
||||||
throws XmlPullParserException, IOException {
|
throws XmlPullParserException, IOException {
|
||||||
|
Uri baseUrl = parentBaseUrl;
|
||||||
int id = -1;
|
int id = -1;
|
||||||
int contentType = AdaptationSet.TYPE_UNKNOWN;
|
int contentType = AdaptationSet.TYPE_UNKNOWN;
|
||||||
|
|
||||||
@ -175,7 +183,9 @@ public class MediaPresentationDescriptionParser extends DefaultHandler {
|
|||||||
do {
|
do {
|
||||||
xpp.next();
|
xpp.next();
|
||||||
if (contentType != AdaptationSet.TYPE_UNKNOWN) {
|
if (contentType != AdaptationSet.TYPE_UNKNOWN) {
|
||||||
if (isStartTag(xpp, "ContentProtection")) {
|
if (isStartTag(xpp, "BaseURL")) {
|
||||||
|
baseUrl = parseBaseUrl(xpp, parentBaseUrl);
|
||||||
|
} else if (isStartTag(xpp, "ContentProtection")) {
|
||||||
if (contentProtections == null) {
|
if (contentProtections == null) {
|
||||||
contentProtections = new ArrayList<ContentProtection>();
|
contentProtections = new ArrayList<ContentProtection>();
|
||||||
}
|
}
|
||||||
@ -187,8 +197,8 @@ public class MediaPresentationDescriptionParser extends DefaultHandler {
|
|||||||
: "audio".equals(contentTypeString) ? AdaptationSet.TYPE_AUDIO
|
: "audio".equals(contentTypeString) ? AdaptationSet.TYPE_AUDIO
|
||||||
: AdaptationSet.TYPE_UNKNOWN;
|
: AdaptationSet.TYPE_UNKNOWN;
|
||||||
} else if (isStartTag(xpp, "Representation")) {
|
} else if (isStartTag(xpp, "Representation")) {
|
||||||
representations.add(parseRepresentation(xpp, contentId, periodStart, periodDuration,
|
representations.add(parseRepresentation(xpp, contentId, baseUrl, periodStart,
|
||||||
mimeType, segmentTimelineList));
|
periodDuration, mimeType, segmentTimelineList));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} while (!isEndTag(xpp, "AdaptationSet"));
|
} while (!isEndTag(xpp, "AdaptationSet"));
|
||||||
@ -208,9 +218,10 @@ public class MediaPresentationDescriptionParser extends DefaultHandler {
|
|||||||
return new ContentProtection(schemeUriId, null);
|
return new ContentProtection(schemeUriId, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Representation parseRepresentation(XmlPullParser xpp, String contentId, long periodStart,
|
private Representation parseRepresentation(XmlPullParser xpp, String contentId, Uri parentBaseUrl,
|
||||||
long periodDuration, String parentMimeType, List<Segment.Timeline> segmentTimelineList)
|
long periodStart, long periodDuration, String parentMimeType,
|
||||||
throws XmlPullParserException, IOException {
|
List<Segment.Timeline> segmentTimelineList) throws XmlPullParserException, IOException {
|
||||||
|
Uri baseUrl = parentBaseUrl;
|
||||||
String id = xpp.getAttributeValue(null, "id");
|
String id = xpp.getAttributeValue(null, "id");
|
||||||
int bandwidth = parseInt(xpp, "bandwidth") / 8;
|
int bandwidth = parseInt(xpp, "bandwidth") / 8;
|
||||||
int audioSamplingRate = parseInt(xpp, "audioSamplingRate");
|
int audioSamplingRate = parseInt(xpp, "audioSamplingRate");
|
||||||
@ -222,7 +233,6 @@ public class MediaPresentationDescriptionParser extends DefaultHandler {
|
|||||||
mimeType = parentMimeType;
|
mimeType = parentMimeType;
|
||||||
}
|
}
|
||||||
|
|
||||||
String representationUrl = null;
|
|
||||||
long indexStart = -1;
|
long indexStart = -1;
|
||||||
long indexEnd = -1;
|
long indexEnd = -1;
|
||||||
long initializationStart = -1;
|
long initializationStart = -1;
|
||||||
@ -232,8 +242,7 @@ public class MediaPresentationDescriptionParser extends DefaultHandler {
|
|||||||
do {
|
do {
|
||||||
xpp.next();
|
xpp.next();
|
||||||
if (isStartTag(xpp, "BaseURL")) {
|
if (isStartTag(xpp, "BaseURL")) {
|
||||||
xpp.next();
|
baseUrl = parseBaseUrl(xpp, parentBaseUrl);
|
||||||
representationUrl = xpp.getText();
|
|
||||||
} else if (isStartTag(xpp, "AudioChannelConfiguration")) {
|
} else if (isStartTag(xpp, "AudioChannelConfiguration")) {
|
||||||
numChannels = Integer.parseInt(xpp.getAttributeValue(null, "value"));
|
numChannels = Integer.parseInt(xpp.getAttributeValue(null, "value"));
|
||||||
} else if (isStartTag(xpp, "SegmentBase")) {
|
} else if (isStartTag(xpp, "SegmentBase")) {
|
||||||
@ -249,15 +258,14 @@ public class MediaPresentationDescriptionParser extends DefaultHandler {
|
|||||||
}
|
}
|
||||||
} while (!isEndTag(xpp, "Representation"));
|
} while (!isEndTag(xpp, "Representation"));
|
||||||
|
|
||||||
Uri uri = Uri.parse(representationUrl);
|
|
||||||
Format format = new Format(id, mimeType, width, height, numChannels, audioSamplingRate,
|
Format format = new Format(id, mimeType, width, height, numChannels, audioSamplingRate,
|
||||||
bandwidth);
|
bandwidth);
|
||||||
if (segmentList == null) {
|
if (segmentList == null) {
|
||||||
return new Representation(contentId, -1, format, uri, DataSpec.LENGTH_UNBOUNDED,
|
return new Representation(contentId, -1, format, baseUrl, DataSpec.LENGTH_UNBOUNDED,
|
||||||
initializationStart, initializationEnd, indexStart, indexEnd, periodStart,
|
initializationStart, initializationEnd, indexStart, indexEnd, periodStart,
|
||||||
periodDuration);
|
periodDuration);
|
||||||
} else {
|
} else {
|
||||||
return new SegmentedRepresentation(contentId, format, uri, initializationStart,
|
return new SegmentedRepresentation(contentId, format, baseUrl, initializationStart,
|
||||||
initializationEnd, indexStart, indexEnd, periodStart, periodDuration, segmentList);
|
initializationEnd, indexStart, indexEnd, periodStart, periodDuration, segmentList);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -321,7 +329,7 @@ public class MediaPresentationDescriptionParser extends DefaultHandler {
|
|||||||
return parseDurationMs(xpp, name, -1);
|
return parseDurationMs(xpp, name, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
private long parseDurationMs(XmlPullParser xpp, String name, long defaultValue) {
|
private static long parseDurationMs(XmlPullParser xpp, String name, long defaultValue) {
|
||||||
String value = xpp.getAttributeValue(null, name);
|
String value = xpp.getAttributeValue(null, name);
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
Matcher matcher = DURATION.matcher(value);
|
Matcher matcher = DURATION.matcher(value);
|
||||||
@ -340,4 +348,16 @@ public class MediaPresentationDescriptionParser extends DefaultHandler {
|
|||||||
return defaultValue;
|
return defaultValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Uri parseBaseUrl(XmlPullParser xpp, Uri parentBaseUrl)
|
||||||
|
throws XmlPullParserException, IOException {
|
||||||
|
xpp.next();
|
||||||
|
String newBaseUrlText = xpp.getText();
|
||||||
|
Uri newBaseUri = Uri.parse(newBaseUrlText);
|
||||||
|
if (newBaseUri.isAbsolute()) {
|
||||||
|
return newBaseUri;
|
||||||
|
} else {
|
||||||
|
return parentBaseUrl.buildUpon().appendEncodedPath(newBaseUrlText).build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,8 @@ package com.google.android.exoplayer.smoothstreaming;
|
|||||||
import com.google.android.exoplayer.ParserException;
|
import com.google.android.exoplayer.ParserException;
|
||||||
import com.google.android.exoplayer.util.ManifestFetcher;
|
import com.google.android.exoplayer.util.ManifestFetcher;
|
||||||
|
|
||||||
|
import android.net.Uri;
|
||||||
|
|
||||||
import org.xmlpull.v1.XmlPullParserException;
|
import org.xmlpull.v1.XmlPullParserException;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -56,7 +58,7 @@ public final class SmoothStreamingManifestFetcher extends ManifestFetcher<Smooth
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected SmoothStreamingManifest parse(InputStream stream, String inputEncoding,
|
protected SmoothStreamingManifest parse(InputStream stream, String inputEncoding,
|
||||||
String contentId) throws IOException, ParserException {
|
String contentId, Uri baseUrl) throws IOException, ParserException {
|
||||||
try {
|
try {
|
||||||
return parser.parse(stream, inputEncoding);
|
return parser.parse(stream, inputEncoding);
|
||||||
} catch (XmlPullParserException e) {
|
} catch (XmlPullParserException e) {
|
||||||
|
@ -17,6 +17,7 @@ package com.google.android.exoplayer.util;
|
|||||||
|
|
||||||
import com.google.android.exoplayer.ParserException;
|
import com.google.android.exoplayer.ParserException;
|
||||||
|
|
||||||
|
import android.net.Uri;
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -84,14 +85,15 @@ public abstract class ManifestFetcher<T> extends AsyncTask<String, Void, T> {
|
|||||||
protected final T doInBackground(String... data) {
|
protected final T doInBackground(String... data) {
|
||||||
try {
|
try {
|
||||||
contentId = data.length > 1 ? data[1] : null;
|
contentId = data.length > 1 ? data[1] : null;
|
||||||
URL url = new URL(data[0]);
|
String urlString = data[0];
|
||||||
String inputEncoding = null;
|
String inputEncoding = null;
|
||||||
InputStream inputStream = null;
|
InputStream inputStream = null;
|
||||||
try {
|
try {
|
||||||
HttpURLConnection connection = configureHttpConnection(url);
|
Uri baseUrl = Util.parseBaseUri(urlString);
|
||||||
|
HttpURLConnection connection = configureHttpConnection(new URL(urlString));
|
||||||
inputStream = connection.getInputStream();
|
inputStream = connection.getInputStream();
|
||||||
inputEncoding = connection.getContentEncoding();
|
inputEncoding = connection.getContentEncoding();
|
||||||
return parse(inputStream, inputEncoding, contentId);
|
return parse(inputStream, inputEncoding, contentId, baseUrl);
|
||||||
} finally {
|
} finally {
|
||||||
if (inputStream != null) {
|
if (inputStream != null) {
|
||||||
inputStream.close();
|
inputStream.close();
|
||||||
@ -119,11 +121,13 @@ public abstract class ManifestFetcher<T> extends AsyncTask<String, Void, T> {
|
|||||||
* @param stream The input stream to read.
|
* @param stream The input stream to read.
|
||||||
* @param inputEncoding The encoding of the input stream.
|
* @param inputEncoding The encoding of the input stream.
|
||||||
* @param contentId The content id of the media.
|
* @param contentId The content id of the media.
|
||||||
|
* @param baseUrl Required where the manifest contains urls that are relative to a base url. May
|
||||||
|
* be null where this is not the case.
|
||||||
* @throws IOException If an error occurred loading the data.
|
* @throws IOException If an error occurred loading the data.
|
||||||
* @throws ParserException If an error occurred parsing the loaded data.
|
* @throws ParserException If an error occurred parsing the loaded data.
|
||||||
*/
|
*/
|
||||||
protected abstract T parse(InputStream stream, String inputEncoding, String contentId) throws
|
protected abstract T parse(InputStream stream, String inputEncoding, String contentId,
|
||||||
IOException, ParserException;
|
Uri baseUrl) throws IOException, ParserException;
|
||||||
|
|
||||||
private HttpURLConnection configureHttpConnection(URL url) throws IOException {
|
private HttpURLConnection configureHttpConnection(URL url) throws IOException {
|
||||||
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
||||||
|
@ -17,6 +17,8 @@ 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 java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
@ -115,6 +117,17 @@ 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('/')));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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…
x
Reference in New Issue
Block a user