Simplify NetworkLoader/Parser/ManifestParser
The only downside of this change is that MediaPresentationDescriptionParser is no longer stateless.
This commit is contained in:
parent
0fdcb3347c
commit
d2da3bbf8a
@ -206,13 +206,13 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback,
|
|||||||
String userAgent = DemoUtil.getUserAgent(this);
|
String userAgent = DemoUtil.getUserAgent(this);
|
||||||
switch (contentType) {
|
switch (contentType) {
|
||||||
case DemoUtil.TYPE_SS:
|
case DemoUtil.TYPE_SS:
|
||||||
return new SmoothStreamingRendererBuilder(userAgent, contentUri.toString(), contentId,
|
return new SmoothStreamingRendererBuilder(userAgent, contentUri.toString(),
|
||||||
new SmoothStreamingTestMediaDrmCallback(), debugTextView);
|
new SmoothStreamingTestMediaDrmCallback(), debugTextView);
|
||||||
case DemoUtil.TYPE_DASH:
|
case DemoUtil.TYPE_DASH:
|
||||||
return new DashRendererBuilder(userAgent, contentUri.toString(), contentId,
|
return new DashRendererBuilder(userAgent, contentUri.toString(),
|
||||||
new WidevineTestMediaDrmCallback(contentId), debugTextView, audioCapabilities);
|
new WidevineTestMediaDrmCallback(contentId), debugTextView, audioCapabilities);
|
||||||
case DemoUtil.TYPE_HLS:
|
case DemoUtil.TYPE_HLS:
|
||||||
return new HlsRendererBuilder(userAgent, contentUri.toString(), contentId);
|
return new HlsRendererBuilder(userAgent, contentUri.toString());
|
||||||
default:
|
default:
|
||||||
return new DefaultRendererBuilder(this, contentUri, debugTextView);
|
return new DefaultRendererBuilder(this, contentUri, debugTextView);
|
||||||
}
|
}
|
||||||
|
@ -93,7 +93,6 @@ public class DashRendererBuilder implements RendererBuilder,
|
|||||||
|
|
||||||
private final String userAgent;
|
private final String userAgent;
|
||||||
private final String url;
|
private final String url;
|
||||||
private final String contentId;
|
|
||||||
private final MediaDrmCallback drmCallback;
|
private final MediaDrmCallback drmCallback;
|
||||||
private final TextView debugTextView;
|
private final TextView debugTextView;
|
||||||
private final AudioCapabilities audioCapabilities;
|
private final AudioCapabilities audioCapabilities;
|
||||||
@ -105,11 +104,10 @@ public class DashRendererBuilder implements RendererBuilder,
|
|||||||
private MediaPresentationDescription manifest;
|
private MediaPresentationDescription manifest;
|
||||||
private long elapsedRealtimeOffset;
|
private long elapsedRealtimeOffset;
|
||||||
|
|
||||||
public DashRendererBuilder(String userAgent, String url, String contentId,
|
public DashRendererBuilder(String userAgent, String url, MediaDrmCallback drmCallback,
|
||||||
MediaDrmCallback drmCallback, TextView debugTextView, AudioCapabilities audioCapabilities) {
|
TextView debugTextView, AudioCapabilities audioCapabilities) {
|
||||||
this.userAgent = userAgent;
|
this.userAgent = userAgent;
|
||||||
this.url = url;
|
this.url = url;
|
||||||
this.contentId = contentId;
|
|
||||||
this.drmCallback = drmCallback;
|
this.drmCallback = drmCallback;
|
||||||
this.debugTextView = debugTextView;
|
this.debugTextView = debugTextView;
|
||||||
this.audioCapabilities = audioCapabilities;
|
this.audioCapabilities = audioCapabilities;
|
||||||
@ -120,13 +118,12 @@ public class DashRendererBuilder implements RendererBuilder,
|
|||||||
this.player = player;
|
this.player = player;
|
||||||
this.callback = callback;
|
this.callback = callback;
|
||||||
MediaPresentationDescriptionParser parser = new MediaPresentationDescriptionParser();
|
MediaPresentationDescriptionParser parser = new MediaPresentationDescriptionParser();
|
||||||
manifestFetcher = new ManifestFetcher<MediaPresentationDescription>(parser, contentId, url,
|
manifestFetcher = new ManifestFetcher<MediaPresentationDescription>(url, userAgent, parser);
|
||||||
userAgent);
|
|
||||||
manifestFetcher.singleLoad(player.getMainHandler().getLooper(), this);
|
manifestFetcher.singleLoad(player.getMainHandler().getLooper(), this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onManifest(String contentId, MediaPresentationDescription manifest) {
|
public void onSingleManifest(MediaPresentationDescription manifest) {
|
||||||
this.manifest = manifest;
|
this.manifest = manifest;
|
||||||
if (manifest.dynamic && manifest.utcTiming != null) {
|
if (manifest.dynamic && manifest.utcTiming != null) {
|
||||||
UtcTimingElementResolver.resolveTimingElement(userAgent, manifest.utcTiming,
|
UtcTimingElementResolver.resolveTimingElement(userAgent, manifest.utcTiming,
|
||||||
@ -137,7 +134,7 @@ public class DashRendererBuilder implements RendererBuilder,
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onManifestError(String contentId, IOException e) {
|
public void onSingleManifestError(IOException e) {
|
||||||
callback.onRenderersError(e);
|
callback.onRenderersError(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,15 +45,13 @@ public class HlsRendererBuilder implements RendererBuilder, ManifestCallback<Hls
|
|||||||
|
|
||||||
private final String userAgent;
|
private final String userAgent;
|
||||||
private final String url;
|
private final String url;
|
||||||
private final String contentId;
|
|
||||||
|
|
||||||
private DemoPlayer player;
|
private DemoPlayer player;
|
||||||
private RendererBuilderCallback callback;
|
private RendererBuilderCallback callback;
|
||||||
|
|
||||||
public HlsRendererBuilder(String userAgent, String url, String contentId) {
|
public HlsRendererBuilder(String userAgent, String url) {
|
||||||
this.userAgent = userAgent;
|
this.userAgent = userAgent;
|
||||||
this.url = url;
|
this.url = url;
|
||||||
this.contentId = contentId;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -62,17 +60,17 @@ public class HlsRendererBuilder implements RendererBuilder, ManifestCallback<Hls
|
|||||||
this.callback = callback;
|
this.callback = callback;
|
||||||
HlsPlaylistParser parser = new HlsPlaylistParser();
|
HlsPlaylistParser parser = new HlsPlaylistParser();
|
||||||
ManifestFetcher<HlsPlaylist> playlistFetcher =
|
ManifestFetcher<HlsPlaylist> playlistFetcher =
|
||||||
new ManifestFetcher<HlsPlaylist>(parser, contentId, url, userAgent);
|
new ManifestFetcher<HlsPlaylist>(url, userAgent, parser);
|
||||||
playlistFetcher.singleLoad(player.getMainHandler().getLooper(), this);
|
playlistFetcher.singleLoad(player.getMainHandler().getLooper(), this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onManifestError(String contentId, IOException e) {
|
public void onSingleManifestError(IOException e) {
|
||||||
callback.onRenderersError(e);
|
callback.onRenderersError(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onManifest(String contentId, HlsPlaylist manifest) {
|
public void onSingleManifest(HlsPlaylist manifest) {
|
||||||
DefaultBandwidthMeter bandwidthMeter = new DefaultBandwidthMeter();
|
DefaultBandwidthMeter bandwidthMeter = new DefaultBandwidthMeter();
|
||||||
|
|
||||||
DataSource dataSource = new UriDataSource(userAgent, bandwidthMeter);
|
DataSource dataSource = new UriDataSource(userAgent, bandwidthMeter);
|
||||||
|
@ -70,7 +70,6 @@ public class SmoothStreamingRendererBuilder implements RendererBuilder,
|
|||||||
|
|
||||||
private final String userAgent;
|
private final String userAgent;
|
||||||
private final String url;
|
private final String url;
|
||||||
private final String contentId;
|
|
||||||
private final MediaDrmCallback drmCallback;
|
private final MediaDrmCallback drmCallback;
|
||||||
private final TextView debugTextView;
|
private final TextView debugTextView;
|
||||||
|
|
||||||
@ -78,11 +77,10 @@ public class SmoothStreamingRendererBuilder implements RendererBuilder,
|
|||||||
private RendererBuilderCallback callback;
|
private RendererBuilderCallback callback;
|
||||||
private ManifestFetcher<SmoothStreamingManifest> manifestFetcher;
|
private ManifestFetcher<SmoothStreamingManifest> manifestFetcher;
|
||||||
|
|
||||||
public SmoothStreamingRendererBuilder(String userAgent, String url, String contentId,
|
public SmoothStreamingRendererBuilder(String userAgent, String url, MediaDrmCallback drmCallback,
|
||||||
MediaDrmCallback drmCallback, TextView debugTextView) {
|
TextView debugTextView) {
|
||||||
this.userAgent = userAgent;
|
this.userAgent = userAgent;
|
||||||
this.url = url;
|
this.url = url;
|
||||||
this.contentId = contentId;
|
|
||||||
this.drmCallback = drmCallback;
|
this.drmCallback = drmCallback;
|
||||||
this.debugTextView = debugTextView;
|
this.debugTextView = debugTextView;
|
||||||
}
|
}
|
||||||
@ -92,18 +90,18 @@ public class SmoothStreamingRendererBuilder implements RendererBuilder,
|
|||||||
this.player = player;
|
this.player = player;
|
||||||
this.callback = callback;
|
this.callback = callback;
|
||||||
SmoothStreamingManifestParser parser = new SmoothStreamingManifestParser();
|
SmoothStreamingManifestParser parser = new SmoothStreamingManifestParser();
|
||||||
manifestFetcher = new ManifestFetcher<SmoothStreamingManifest>(parser, contentId,
|
manifestFetcher = new ManifestFetcher<SmoothStreamingManifest>(url + "/Manifest", userAgent,
|
||||||
url + "/Manifest", userAgent);
|
parser);
|
||||||
manifestFetcher.singleLoad(player.getMainHandler().getLooper(), this);
|
manifestFetcher.singleLoad(player.getMainHandler().getLooper(), this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onManifestError(String contentId, IOException exception) {
|
public void onSingleManifestError(IOException exception) {
|
||||||
callback.onRenderersError(exception);
|
callback.onRenderersError(exception);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onManifest(String contentId, SmoothStreamingManifest manifest) {
|
public void onSingleManifest(SmoothStreamingManifest manifest) {
|
||||||
Handler mainHandler = player.getMainHandler();
|
Handler mainHandler = player.getMainHandler();
|
||||||
LoadControl loadControl = new DefaultLoadControl(new BufferPool(BUFFER_SEGMENT_SIZE));
|
LoadControl loadControl = new DefaultLoadControl(new BufferPool(BUFFER_SEGMENT_SIZE));
|
||||||
DefaultBandwidthMeter bandwidthMeter = new DefaultBandwidthMeter(mainHandler, player);
|
DefaultBandwidthMeter bandwidthMeter = new DefaultBandwidthMeter(mainHandler, player);
|
||||||
|
@ -22,8 +22,8 @@ import com.google.android.exoplayer.dash.mpd.SegmentBase.SegmentTemplate;
|
|||||||
import com.google.android.exoplayer.dash.mpd.SegmentBase.SegmentTimelineElement;
|
import com.google.android.exoplayer.dash.mpd.SegmentBase.SegmentTimelineElement;
|
||||||
import com.google.android.exoplayer.dash.mpd.SegmentBase.SingleSegmentBase;
|
import com.google.android.exoplayer.dash.mpd.SegmentBase.SingleSegmentBase;
|
||||||
import com.google.android.exoplayer.util.Assertions;
|
import com.google.android.exoplayer.util.Assertions;
|
||||||
import com.google.android.exoplayer.util.ManifestParser;
|
|
||||||
import com.google.android.exoplayer.util.MimeTypes;
|
import com.google.android.exoplayer.util.MimeTypes;
|
||||||
|
import com.google.android.exoplayer.util.NetworkLoadable;
|
||||||
import com.google.android.exoplayer.util.Util;
|
import com.google.android.exoplayer.util.Util;
|
||||||
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
@ -44,11 +44,25 @@ import java.util.List;
|
|||||||
* A parser of media presentation description files.
|
* A parser of media presentation description files.
|
||||||
*/
|
*/
|
||||||
public class MediaPresentationDescriptionParser extends DefaultHandler
|
public class MediaPresentationDescriptionParser extends DefaultHandler
|
||||||
implements ManifestParser<MediaPresentationDescription> {
|
implements NetworkLoadable.Parser<MediaPresentationDescription> {
|
||||||
|
|
||||||
|
private final String contentId;
|
||||||
private final XmlPullParserFactory xmlParserFactory;
|
private final XmlPullParserFactory xmlParserFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Equivalent to calling {@code new MediaPresentationDescriptionParser(null)}.
|
||||||
|
*/
|
||||||
public MediaPresentationDescriptionParser() {
|
public MediaPresentationDescriptionParser() {
|
||||||
|
this(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param contentId An optional content identifier to include in the parsed manifest.
|
||||||
|
*/
|
||||||
|
// TODO: Remove the need to inject a content identifier here, by not including it in the parsed
|
||||||
|
// manifest. Instead, it should be injected directly where needed (i.e. DashChunkSource).
|
||||||
|
public MediaPresentationDescriptionParser(String contentId) {
|
||||||
|
this.contentId = contentId;
|
||||||
try {
|
try {
|
||||||
xmlParserFactory = XmlPullParserFactory.newInstance();
|
xmlParserFactory = XmlPullParserFactory.newInstance();
|
||||||
} catch (XmlPullParserException e) {
|
} catch (XmlPullParserException e) {
|
||||||
@ -59,8 +73,8 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
|
|||||||
// MPD parsing.
|
// MPD parsing.
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MediaPresentationDescription parse(InputStream inputStream, String inputEncoding,
|
public MediaPresentationDescription parse(String connectionUrl, InputStream inputStream,
|
||||||
String contentId, Uri baseUrl) throws IOException, ParserException {
|
String inputEncoding) throws IOException, ParserException {
|
||||||
try {
|
try {
|
||||||
XmlPullParser xpp = xmlParserFactory.newPullParser();
|
XmlPullParser xpp = xmlParserFactory.newPullParser();
|
||||||
xpp.setInput(inputStream, inputEncoding);
|
xpp.setInput(inputStream, inputEncoding);
|
||||||
@ -69,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, contentId, baseUrl);
|
return parseMediaPresentationDescription(xpp, Util.parseBaseUri(connectionUrl));
|
||||||
} catch (XmlPullParserException e) {
|
} catch (XmlPullParserException e) {
|
||||||
throw new ParserException(e);
|
throw new ParserException(e);
|
||||||
} catch (ParseException e) {
|
} catch (ParseException e) {
|
||||||
@ -78,7 +92,7 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected MediaPresentationDescription parseMediaPresentationDescription(XmlPullParser xpp,
|
protected MediaPresentationDescription parseMediaPresentationDescription(XmlPullParser xpp,
|
||||||
String contentId, Uri baseUrl) throws XmlPullParserException, IOException, ParseException {
|
Uri 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);
|
||||||
@ -97,7 +111,7 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
|
|||||||
} else if (isStartTag(xpp, "UTCTiming")) {
|
} else if (isStartTag(xpp, "UTCTiming")) {
|
||||||
utcTiming = parseUtcTiming(xpp);
|
utcTiming = parseUtcTiming(xpp);
|
||||||
} else if (isStartTag(xpp, "Period")) {
|
} else if (isStartTag(xpp, "Period")) {
|
||||||
periods.add(parsePeriod(xpp, contentId, baseUrl, durationMs));
|
periods.add(parsePeriod(xpp, baseUrl, durationMs));
|
||||||
}
|
}
|
||||||
} while (!isEndTag(xpp, "MPD"));
|
} while (!isEndTag(xpp, "MPD"));
|
||||||
|
|
||||||
@ -123,7 +137,7 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
|
|||||||
return new UtcTimingElement(schemeIdUri, value);
|
return new UtcTimingElement(schemeIdUri, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Period parsePeriod(XmlPullParser xpp, String contentId, Uri baseUrl, long mpdDurationMs)
|
protected Period parsePeriod(XmlPullParser xpp, Uri 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);
|
||||||
@ -135,7 +149,7 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
|
|||||||
if (isStartTag(xpp, "BaseURL")) {
|
if (isStartTag(xpp, "BaseURL")) {
|
||||||
baseUrl = parseBaseUrl(xpp, baseUrl);
|
baseUrl = parseBaseUrl(xpp, baseUrl);
|
||||||
} else if (isStartTag(xpp, "AdaptationSet")) {
|
} else if (isStartTag(xpp, "AdaptationSet")) {
|
||||||
adaptationSets.add(parseAdaptationSet(xpp, contentId, baseUrl, startMs, durationMs,
|
adaptationSets.add(parseAdaptationSet(xpp, baseUrl, startMs, durationMs,
|
||||||
segmentBase));
|
segmentBase));
|
||||||
} else if (isStartTag(xpp, "SegmentBase")) {
|
} else if (isStartTag(xpp, "SegmentBase")) {
|
||||||
segmentBase = parseSegmentBase(xpp, baseUrl, null);
|
segmentBase = parseSegmentBase(xpp, baseUrl, null);
|
||||||
@ -156,9 +170,8 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
|
|||||||
|
|
||||||
// AdaptationSet parsing.
|
// AdaptationSet parsing.
|
||||||
|
|
||||||
protected AdaptationSet parseAdaptationSet(XmlPullParser xpp, String contentId, Uri baseUrl,
|
protected AdaptationSet parseAdaptationSet(XmlPullParser xpp, Uri baseUrl, long periodStartMs,
|
||||||
long periodStartMs, long periodDurationMs, SegmentBase segmentBase)
|
long periodDurationMs, SegmentBase segmentBase) throws XmlPullParserException, IOException {
|
||||||
throws XmlPullParserException, IOException {
|
|
||||||
|
|
||||||
String mimeType = xpp.getAttributeValue(null, "mimeType");
|
String mimeType = xpp.getAttributeValue(null, "mimeType");
|
||||||
String language = xpp.getAttributeValue(null, "lang");
|
String language = xpp.getAttributeValue(null, "lang");
|
||||||
@ -181,7 +194,7 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
|
|||||||
contentType = checkAdaptationSetTypeConsistency(contentType,
|
contentType = checkAdaptationSetTypeConsistency(contentType,
|
||||||
parseAdaptationSetType(xpp.getAttributeValue(null, "contentType")));
|
parseAdaptationSetType(xpp.getAttributeValue(null, "contentType")));
|
||||||
} else if (isStartTag(xpp, "Representation")) {
|
} else if (isStartTag(xpp, "Representation")) {
|
||||||
Representation representation = parseRepresentation(xpp, contentId, baseUrl, periodStartMs,
|
Representation representation = parseRepresentation(xpp, baseUrl, periodStartMs,
|
||||||
periodDurationMs, mimeType, language, segmentBase);
|
periodDurationMs, mimeType, language, segmentBase);
|
||||||
contentType = checkAdaptationSetTypeConsistency(contentType,
|
contentType = checkAdaptationSetTypeConsistency(contentType,
|
||||||
parseAdaptationSetTypeFromMimeType(representation.format.mimeType));
|
parseAdaptationSetTypeFromMimeType(representation.format.mimeType));
|
||||||
@ -274,9 +287,9 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
|
|||||||
|
|
||||||
// Representation parsing.
|
// Representation parsing.
|
||||||
|
|
||||||
protected Representation parseRepresentation(XmlPullParser xpp, String contentId, Uri baseUrl,
|
protected Representation parseRepresentation(XmlPullParser xpp, Uri baseUrl, long periodStartMs,
|
||||||
long periodStartMs, long periodDurationMs, String mimeType, String language,
|
long periodDurationMs, String mimeType, String language, SegmentBase segmentBase)
|
||||||
SegmentBase segmentBase) throws XmlPullParserException, IOException {
|
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");
|
||||||
|
@ -62,16 +62,13 @@ public class UtcTimingElementResolver implements Loader.Callback {
|
|||||||
void onTimestampError(UtcTimingElement utcTiming, IOException e);
|
void onTimestampError(UtcTimingElement utcTiming, IOException e);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final int TYPE_XS = 0;
|
|
||||||
private static final int TYPE_ISO = 1;
|
|
||||||
|
|
||||||
private final String userAgent;
|
private final String userAgent;
|
||||||
private final UtcTimingElement timingElement;
|
private final UtcTimingElement timingElement;
|
||||||
private final long timingElementElapsedRealtime;
|
private final long timingElementElapsedRealtime;
|
||||||
private final UtcTimingCallback callback;
|
private final UtcTimingCallback callback;
|
||||||
|
|
||||||
private Loader singleUseLoader;
|
private Loader singleUseLoader;
|
||||||
private HttpTimestampLoadable singleUseLoadable;
|
private NetworkLoadable<Long> singleUseLoadable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolves a {@link UtcTimingElement}.
|
* Resolves a {@link UtcTimingElement}.
|
||||||
@ -102,10 +99,10 @@ public class UtcTimingElementResolver implements Loader.Callback {
|
|||||||
if (Util.areEqual(scheme, "urn:mpeg:dash:utc:direct:2012")) {
|
if (Util.areEqual(scheme, "urn:mpeg:dash:utc:direct:2012")) {
|
||||||
resolveDirect();
|
resolveDirect();
|
||||||
} else if (Util.areEqual(scheme, "urn:mpeg:dash:utc:http-iso:2014")) {
|
} else if (Util.areEqual(scheme, "urn:mpeg:dash:utc:http-iso:2014")) {
|
||||||
resolveHttp(TYPE_ISO);
|
resolveHttp(new Iso8601Parser());
|
||||||
} else if (Util.areEqual(scheme, "urn:mpeg:dash:utc:http-xsdate:2012")
|
} else if (Util.areEqual(scheme, "urn:mpeg:dash:utc:http-xsdate:2012")
|
||||||
|| Util.areEqual(scheme, "urn:mpeg:dash:utc:http-xsdate:2014")) {
|
|| Util.areEqual(scheme, "urn:mpeg:dash:utc:http-xsdate:2014")) {
|
||||||
resolveHttp(TYPE_XS);
|
resolveHttp(new XsDateTimeParser());
|
||||||
} else {
|
} else {
|
||||||
// Unsupported scheme.
|
// Unsupported scheme.
|
||||||
callback.onTimestampError(timingElement, new IOException("Unsupported utc timing scheme"));
|
callback.onTimestampError(timingElement, new IOException("Unsupported utc timing scheme"));
|
||||||
@ -122,9 +119,9 @@ public class UtcTimingElementResolver implements Loader.Callback {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void resolveHttp(int type) {
|
private void resolveHttp(NetworkLoadable.Parser<Long> parser) {
|
||||||
singleUseLoader = new Loader("utctiming");
|
singleUseLoader = new Loader("utctiming");
|
||||||
singleUseLoadable = new HttpTimestampLoadable(timingElement.value, userAgent, type);
|
singleUseLoadable = new NetworkLoadable<Long>(timingElement.value, userAgent, parser);
|
||||||
singleUseLoader.startLoading(singleUseLoadable, this);
|
singleUseLoader.startLoading(singleUseLoadable, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -150,32 +147,31 @@ public class UtcTimingElementResolver implements Loader.Callback {
|
|||||||
singleUseLoader.release();
|
singleUseLoader.release();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class HttpTimestampLoadable extends NetworkLoadable<Long> {
|
private static class XsDateTimeParser implements NetworkLoadable.Parser<Long> {
|
||||||
|
|
||||||
private final int type;
|
|
||||||
|
|
||||||
public HttpTimestampLoadable(String url, String userAgent, int type) {
|
|
||||||
super(url, userAgent);
|
|
||||||
this.type = type;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Long parse(String connectionUrl, InputStream inputStream, String inputEncoding)
|
public Long parse(String connectionUrl, InputStream inputStream, String inputEncoding)
|
||||||
throws ParserException, IOException {
|
throws ParserException, IOException {
|
||||||
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
|
String firstLine = new BufferedReader(new InputStreamReader(inputStream)).readLine();
|
||||||
String firstLine = reader.readLine();
|
|
||||||
try {
|
try {
|
||||||
switch (type) {
|
return Util.parseXsDateTime(firstLine);
|
||||||
case TYPE_XS:
|
} catch (ParseException e) {
|
||||||
return Util.parseXsDateTime(firstLine);
|
throw new ParserException(e);
|
||||||
case TYPE_ISO:
|
}
|
||||||
// TODO: It may be necessary to handle timestamp offsets from UTC.
|
}
|
||||||
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US);
|
|
||||||
return format.parse(firstLine).getTime();
|
}
|
||||||
default:
|
|
||||||
// Never happens.
|
private static class Iso8601Parser implements NetworkLoadable.Parser<Long> {
|
||||||
throw new RuntimeException();
|
|
||||||
}
|
@Override
|
||||||
|
public Long parse(String connectionUrl, InputStream inputStream, String inputEncoding)
|
||||||
|
throws ParserException, IOException {
|
||||||
|
String firstLine = new BufferedReader(new InputStreamReader(inputStream)).readLine();
|
||||||
|
try {
|
||||||
|
// TODO: It may be necessary to handle timestamp offsets from UTC.
|
||||||
|
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US);
|
||||||
|
return format.parse(firstLine).getTime();
|
||||||
} catch (ParseException e) {
|
} catch (ParseException e) {
|
||||||
throw new ParserException(e);
|
throw new ParserException(e);
|
||||||
}
|
}
|
||||||
|
@ -433,8 +433,8 @@ public class HlsChunkSource {
|
|||||||
private MediaPlaylistChunk newMediaPlaylistChunk(int variantIndex) {
|
private MediaPlaylistChunk newMediaPlaylistChunk(int variantIndex) {
|
||||||
Uri mediaPlaylistUri = Util.getMergedUri(baseUri, enabledVariants[variantIndex].url);
|
Uri mediaPlaylistUri = Util.getMergedUri(baseUri, enabledVariants[variantIndex].url);
|
||||||
DataSpec dataSpec = new DataSpec(mediaPlaylistUri, 0, C.LENGTH_UNBOUNDED, null);
|
DataSpec dataSpec = new DataSpec(mediaPlaylistUri, 0, C.LENGTH_UNBOUNDED, null);
|
||||||
Uri baseUri = Util.parseBaseUri(mediaPlaylistUri.toString());
|
return new MediaPlaylistChunk(variantIndex, upstreamDataSource, dataSpec,
|
||||||
return new MediaPlaylistChunk(variantIndex, upstreamDataSource, dataSpec, baseUri);
|
mediaPlaylistUri.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
private EncryptionKeyChunk newEncryptionKeyChunk(Uri keyUri, String iv) {
|
private EncryptionKeyChunk newEncryptionKeyChunk(Uri keyUri, String iv) {
|
||||||
@ -546,19 +546,19 @@ public class HlsChunkSource {
|
|||||||
@SuppressWarnings("hiding")
|
@SuppressWarnings("hiding")
|
||||||
/* package */ final int variantIndex;
|
/* package */ final int variantIndex;
|
||||||
|
|
||||||
private final Uri playlistBaseUri;
|
private final String playlistUrl;
|
||||||
|
|
||||||
public MediaPlaylistChunk(int variantIndex, DataSource dataSource, DataSpec dataSpec,
|
public MediaPlaylistChunk(int variantIndex, DataSource dataSource, DataSpec dataSpec,
|
||||||
Uri playlistBaseUri) {
|
String playlistUrl) {
|
||||||
super(dataSource, dataSpec, scratchSpace);
|
super(dataSource, dataSpec, scratchSpace);
|
||||||
this.variantIndex = variantIndex;
|
this.variantIndex = variantIndex;
|
||||||
this.playlistBaseUri = playlistBaseUri;
|
this.playlistUrl = playlistUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void consume(byte[] data, int limit) throws IOException {
|
protected void consume(byte[] data, int limit) throws IOException {
|
||||||
HlsPlaylist playlist = playlistParser.parse(new ByteArrayInputStream(data, 0, limit),
|
HlsPlaylist playlist = playlistParser.parse(playlistUrl,
|
||||||
null, null, playlistBaseUri);
|
new ByteArrayInputStream(data, 0, limit), null);
|
||||||
Assertions.checkState(playlist.type == HlsPlaylist.TYPE_MEDIA);
|
Assertions.checkState(playlist.type == HlsPlaylist.TYPE_MEDIA);
|
||||||
HlsMediaPlaylist mediaPlaylist = (HlsMediaPlaylist) playlist;
|
HlsMediaPlaylist mediaPlaylist = (HlsMediaPlaylist) playlist;
|
||||||
setMediaPlaylist(variantIndex, mediaPlaylist);
|
setMediaPlaylist(variantIndex, mediaPlaylist);
|
||||||
|
@ -18,7 +18,8 @@ package com.google.android.exoplayer.hls;
|
|||||||
import com.google.android.exoplayer.C;
|
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.util.ManifestParser;
|
import com.google.android.exoplayer.util.NetworkLoadable;
|
||||||
|
import com.google.android.exoplayer.util.Util;
|
||||||
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
|
||||||
@ -36,7 +37,7 @@ import java.util.regex.Pattern;
|
|||||||
/**
|
/**
|
||||||
* HLS playlists parsing logic.
|
* HLS playlists parsing logic.
|
||||||
*/
|
*/
|
||||||
public final class HlsPlaylistParser implements ManifestParser<HlsPlaylist> {
|
public final class HlsPlaylistParser implements NetworkLoadable.Parser<HlsPlaylist> {
|
||||||
|
|
||||||
private static final String VERSION_TAG = "#EXT-X-VERSION";
|
private static final String VERSION_TAG = "#EXT-X-VERSION";
|
||||||
|
|
||||||
@ -83,8 +84,9 @@ public final class HlsPlaylistParser implements ManifestParser<HlsPlaylist> {
|
|||||||
Pattern.compile(IV_ATTR + "=([^,.*]+)");
|
Pattern.compile(IV_ATTR + "=([^,.*]+)");
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public HlsPlaylist parse(InputStream inputStream, String inputEncoding,
|
public HlsPlaylist parse(String connectionUrl, InputStream inputStream, String inputEncoding)
|
||||||
String contentId, Uri baseUri) throws IOException {
|
throws IOException, ParserException {
|
||||||
|
Uri baseUri = Util.parseBaseUri(connectionUrl);
|
||||||
BufferedReader reader = new BufferedReader((inputEncoding == null)
|
BufferedReader reader = new BufferedReader((inputEncoding == null)
|
||||||
? new InputStreamReader(inputStream) : new InputStreamReader(inputStream, inputEncoding));
|
? new InputStreamReader(inputStream) : new InputStreamReader(inputStream, inputEncoding));
|
||||||
Queue<String> extraLines = new LinkedList<String>();
|
Queue<String> extraLines = new LinkedList<String>();
|
||||||
|
@ -21,8 +21,9 @@ import com.google.android.exoplayer.smoothstreaming.SmoothStreamingManifest.Stre
|
|||||||
import com.google.android.exoplayer.smoothstreaming.SmoothStreamingManifest.TrackElement;
|
import com.google.android.exoplayer.smoothstreaming.SmoothStreamingManifest.TrackElement;
|
||||||
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.ManifestParser;
|
|
||||||
import com.google.android.exoplayer.util.MimeTypes;
|
import com.google.android.exoplayer.util.MimeTypes;
|
||||||
|
import com.google.android.exoplayer.util.NetworkLoadable;
|
||||||
|
import com.google.android.exoplayer.util.Util;
|
||||||
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.util.Base64;
|
import android.util.Base64;
|
||||||
@ -45,7 +46,8 @@ import java.util.UUID;
|
|||||||
* @see <a href="http://msdn.microsoft.com/en-us/library/ee673436(v=vs.90).aspx">
|
* @see <a href="http://msdn.microsoft.com/en-us/library/ee673436(v=vs.90).aspx">
|
||||||
* IIS Smooth Streaming Client Manifest Format</a>
|
* IIS Smooth Streaming Client Manifest Format</a>
|
||||||
*/
|
*/
|
||||||
public class SmoothStreamingManifestParser implements ManifestParser<SmoothStreamingManifest> {
|
public class SmoothStreamingManifestParser implements
|
||||||
|
NetworkLoadable.Parser<SmoothStreamingManifest> {
|
||||||
|
|
||||||
private final XmlPullParserFactory xmlParserFactory;
|
private final XmlPullParserFactory xmlParserFactory;
|
||||||
|
|
||||||
@ -58,12 +60,13 @@ public class SmoothStreamingManifestParser implements ManifestParser<SmoothStrea
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SmoothStreamingManifest parse(InputStream inputStream, String inputEncoding,
|
public SmoothStreamingManifest parse(String connectionUrl, InputStream inputStream,
|
||||||
String contentId, Uri baseUri) throws IOException, ParserException {
|
String inputEncoding) throws IOException, ParserException {
|
||||||
try {
|
try {
|
||||||
XmlPullParser xmlParser = xmlParserFactory.newPullParser();
|
XmlPullParser xmlParser = xmlParserFactory.newPullParser();
|
||||||
xmlParser.setInput(inputStream, inputEncoding);
|
xmlParser.setInput(inputStream, inputEncoding);
|
||||||
SmoothStreamMediaParser smoothStreamMediaParser = new SmoothStreamMediaParser(null, baseUri);
|
SmoothStreamMediaParser smoothStreamMediaParser = new SmoothStreamMediaParser(null,
|
||||||
|
Util.parseBaseUri(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);
|
||||||
|
@ -15,7 +15,6 @@
|
|||||||
*/
|
*/
|
||||||
package com.google.android.exoplayer.util;
|
package com.google.android.exoplayer.util;
|
||||||
|
|
||||||
import com.google.android.exoplayer.ParserException;
|
|
||||||
import com.google.android.exoplayer.upstream.Loader;
|
import com.google.android.exoplayer.upstream.Loader;
|
||||||
import com.google.android.exoplayer.upstream.Loader.Loadable;
|
import com.google.android.exoplayer.upstream.Loader.Loadable;
|
||||||
|
|
||||||
@ -25,7 +24,6 @@ import android.os.SystemClock;
|
|||||||
import android.util.Pair;
|
import android.util.Pair;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
|
||||||
import java.util.concurrent.CancellationException;
|
import java.util.concurrent.CancellationException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -58,24 +56,21 @@ public class ManifestFetcher<T> implements Loader.Callback {
|
|||||||
/**
|
/**
|
||||||
* Invoked when the load has successfully completed.
|
* Invoked when the load has successfully completed.
|
||||||
*
|
*
|
||||||
* @param contentId The content id of the media.
|
|
||||||
* @param manifest The loaded manifest.
|
* @param manifest The loaded manifest.
|
||||||
*/
|
*/
|
||||||
void onManifest(String contentId, T manifest);
|
void onSingleManifest(T manifest);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invoked when the load has failed.
|
* Invoked when the load has failed.
|
||||||
*
|
*
|
||||||
* @param contentId The content id of the media.
|
|
||||||
* @param e The cause of the failure.
|
* @param e The cause of the failure.
|
||||||
*/
|
*/
|
||||||
void onManifestError(String contentId, IOException e);
|
void onSingleManifestError(IOException e);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* package */ final ManifestParser<T> parser;
|
private final NetworkLoadable.Parser<T> parser;
|
||||||
/* package */ final String contentId;
|
private final String userAgent;
|
||||||
/* package */ final String userAgent;
|
|
||||||
private final Handler eventHandler;
|
private final Handler eventHandler;
|
||||||
private final EventListener eventListener;
|
private final EventListener eventListener;
|
||||||
|
|
||||||
@ -83,7 +78,7 @@ public class ManifestFetcher<T> implements Loader.Callback {
|
|||||||
|
|
||||||
private int enabledCount;
|
private int enabledCount;
|
||||||
private Loader loader;
|
private Loader loader;
|
||||||
private ManifestLoadable<T> currentLoadable;
|
private NetworkLoadable<T> currentLoadable;
|
||||||
|
|
||||||
private int loadExceptionCount;
|
private int loadExceptionCount;
|
||||||
private long loadExceptionTimestamp;
|
private long loadExceptionTimestamp;
|
||||||
@ -92,21 +87,26 @@ public class ManifestFetcher<T> implements Loader.Callback {
|
|||||||
private volatile T manifest;
|
private volatile T manifest;
|
||||||
private volatile long manifestLoadTimestamp;
|
private volatile long manifestLoadTimestamp;
|
||||||
|
|
||||||
public ManifestFetcher(ManifestParser<T> parser, String contentId, String manifestUrl,
|
/**
|
||||||
String userAgent) {
|
* @param manifestUrl The manifest location.
|
||||||
this(parser, contentId, manifestUrl, userAgent, null, null);
|
* @param userAgent The User-Agent string that should be used.
|
||||||
|
* @param parser A parser to parse the loaded manifest data.
|
||||||
|
*/
|
||||||
|
public ManifestFetcher(String manifestUrl, String userAgent, NetworkLoadable.Parser<T> parser) {
|
||||||
|
this(manifestUrl, userAgent, parser, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param parser A parser to parse the loaded manifest data.
|
|
||||||
* @param contentId The content id of the content being loaded. May be null.
|
|
||||||
* @param manifestUrl The manifest location.
|
* @param manifestUrl The manifest location.
|
||||||
* @param userAgent The User-Agent string that should be used.
|
* @param userAgent The User-Agent string that should be used.
|
||||||
|
* @param parser A parser to parse the loaded manifest data.
|
||||||
|
* @param eventHandler A handler to use when delivering events to {@code eventListener}. May be
|
||||||
|
* null if delivery of events is not required.
|
||||||
|
* @param eventListener A listener of events. May be null if delivery of events is not required.
|
||||||
*/
|
*/
|
||||||
public ManifestFetcher(ManifestParser<T> parser, String contentId, String manifestUrl,
|
public ManifestFetcher(String manifestUrl, String userAgent, NetworkLoadable.Parser<T> parser,
|
||||||
String userAgent, Handler eventHandler, EventListener eventListener) {
|
Handler eventHandler, EventListener eventListener) {
|
||||||
this.parser = parser;
|
this.parser = parser;
|
||||||
this.contentId = contentId;
|
|
||||||
this.manifestUrl = manifestUrl;
|
this.manifestUrl = manifestUrl;
|
||||||
this.userAgent = userAgent;
|
this.userAgent = userAgent;
|
||||||
this.eventHandler = eventHandler;
|
this.eventHandler = eventHandler;
|
||||||
@ -130,7 +130,8 @@ public class ManifestFetcher<T> implements Loader.Callback {
|
|||||||
* @param callback The callback to receive the result.
|
* @param callback The callback to receive the result.
|
||||||
*/
|
*/
|
||||||
public void singleLoad(Looper callbackLooper, final ManifestCallback<T> callback) {
|
public void singleLoad(Looper callbackLooper, final ManifestCallback<T> callback) {
|
||||||
SingleFetchHelper fetchHelper = new SingleFetchHelper(callbackLooper, callback);
|
SingleFetchHelper fetchHelper = new SingleFetchHelper(
|
||||||
|
new NetworkLoadable<T>(manifestUrl, userAgent, parser), callbackLooper, callback);
|
||||||
fetchHelper.startLoading();
|
fetchHelper.startLoading();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -203,7 +204,7 @@ public class ManifestFetcher<T> implements Loader.Callback {
|
|||||||
loader = new Loader("manifestLoader");
|
loader = new Loader("manifestLoader");
|
||||||
}
|
}
|
||||||
if (!loader.isLoading()) {
|
if (!loader.isLoading()) {
|
||||||
currentLoadable = new ManifestLoadable<T>(manifestUrl, userAgent, contentId, parser);
|
currentLoadable = new NetworkLoadable<T>(manifestUrl, userAgent, parser);
|
||||||
loader.startLoading(currentLoadable, this);
|
loader.startLoading(currentLoadable, this);
|
||||||
notifyManifestRefreshStarted();
|
notifyManifestRefreshStarted();
|
||||||
}
|
}
|
||||||
@ -287,16 +288,17 @@ public class ManifestFetcher<T> implements Loader.Callback {
|
|||||||
|
|
||||||
private class SingleFetchHelper implements Loader.Callback {
|
private class SingleFetchHelper implements Loader.Callback {
|
||||||
|
|
||||||
|
private final NetworkLoadable<T> singleUseLoadable;
|
||||||
private final Looper callbackLooper;
|
private final Looper callbackLooper;
|
||||||
private final ManifestCallback<T> wrappedCallback;
|
private final ManifestCallback<T> wrappedCallback;
|
||||||
private final Loader singleUseLoader;
|
private final Loader singleUseLoader;
|
||||||
private final ManifestLoadable<T> singleUseLoadable;
|
|
||||||
|
|
||||||
public SingleFetchHelper(Looper callbackLooper, ManifestCallback<T> wrappedCallback) {
|
public SingleFetchHelper(NetworkLoadable<T> singleUseLoadable, Looper callbackLooper,
|
||||||
|
ManifestCallback<T> wrappedCallback) {
|
||||||
|
this.singleUseLoadable = singleUseLoadable;
|
||||||
this.callbackLooper = callbackLooper;
|
this.callbackLooper = callbackLooper;
|
||||||
this.wrappedCallback = wrappedCallback;
|
this.wrappedCallback = wrappedCallback;
|
||||||
singleUseLoader = new Loader("manifestLoader:single");
|
singleUseLoader = new Loader("manifestLoader:single");
|
||||||
singleUseLoadable = new ManifestLoadable<T>(manifestUrl, userAgent, contentId, parser);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void startLoading() {
|
public void startLoading() {
|
||||||
@ -308,7 +310,7 @@ public class ManifestFetcher<T> implements Loader.Callback {
|
|||||||
try {
|
try {
|
||||||
T result = singleUseLoadable.getResult();
|
T result = singleUseLoadable.getResult();
|
||||||
onSingleFetchCompleted(result);
|
onSingleFetchCompleted(result);
|
||||||
wrappedCallback.onManifest(contentId, result);
|
wrappedCallback.onSingleManifest(result);
|
||||||
} finally {
|
} finally {
|
||||||
releaseLoader();
|
releaseLoader();
|
||||||
}
|
}
|
||||||
@ -319,7 +321,7 @@ public class ManifestFetcher<T> implements Loader.Callback {
|
|||||||
// This shouldn't ever happen, but handle it anyway.
|
// This shouldn't ever happen, but handle it anyway.
|
||||||
try {
|
try {
|
||||||
IOException exception = new IOException("Load cancelled", new CancellationException());
|
IOException exception = new IOException("Load cancelled", new CancellationException());
|
||||||
wrappedCallback.onManifestError(contentId, exception);
|
wrappedCallback.onSingleManifestError(exception);
|
||||||
} finally {
|
} finally {
|
||||||
releaseLoader();
|
releaseLoader();
|
||||||
}
|
}
|
||||||
@ -328,7 +330,7 @@ public class ManifestFetcher<T> implements Loader.Callback {
|
|||||||
@Override
|
@Override
|
||||||
public void onLoadError(Loadable loadable, IOException exception) {
|
public void onLoadError(Loadable loadable, IOException exception) {
|
||||||
try {
|
try {
|
||||||
wrappedCallback.onManifestError(contentId, exception);
|
wrappedCallback.onSingleManifestError(exception);
|
||||||
} finally {
|
} finally {
|
||||||
releaseLoader();
|
releaseLoader();
|
||||||
}
|
}
|
||||||
@ -340,24 +342,4 @@ public class ManifestFetcher<T> implements Loader.Callback {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class ManifestLoadable<T> extends NetworkLoadable<T> {
|
|
||||||
|
|
||||||
private final String contentId;
|
|
||||||
private final ManifestParser<T> parser;
|
|
||||||
|
|
||||||
public ManifestLoadable(String url, String userAgent, String contentId,
|
|
||||||
ManifestParser<T> parser) {
|
|
||||||
super(url, userAgent);
|
|
||||||
this.contentId = contentId;
|
|
||||||
this.parser = parser;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected T parse(String connectionUrl, InputStream inputStream, String inputEncoding)
|
|
||||||
throws ParserException, IOException {
|
|
||||||
return parser.parse(inputStream, inputEncoding, contentId, Util.parseBaseUri(connectionUrl));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,48 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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 com.google.android.exoplayer.ParserException;
|
|
||||||
|
|
||||||
import android.net.Uri;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parses a manifest from an {@link InputStream}.
|
|
||||||
*
|
|
||||||
* @param <T> The type of the manifest being parsed.
|
|
||||||
*/
|
|
||||||
public interface ManifestParser<T> {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parses a manifest from an {@link InputStream}.
|
|
||||||
*
|
|
||||||
* @param inputStream The input stream to consume.
|
|
||||||
* @param inputEncoding The encoding of the input stream. May be null if the input encoding is
|
|
||||||
* unknown.
|
|
||||||
* @param contentId The content id to which the manifest corresponds. May be null.
|
|
||||||
* @param baseUri If the manifest contains relative uris, this is the uri they are relative to.
|
|
||||||
* May be null.
|
|
||||||
* @return The parsed manifest.
|
|
||||||
* @throws IOException If an error occurs reading the data.
|
|
||||||
* @throws ParserException If an error occurs parsing the data.
|
|
||||||
*/
|
|
||||||
T parse(InputStream inputStream, String inputEncoding, String contentId, Uri baseUri)
|
|
||||||
throws IOException, ParserException;
|
|
||||||
|
|
||||||
}
|
|
@ -28,13 +28,34 @@ import java.net.URLConnection;
|
|||||||
*
|
*
|
||||||
* @param <T> The type of the object being loaded.
|
* @param <T> The type of the object being loaded.
|
||||||
*/
|
*/
|
||||||
public abstract class NetworkLoadable<T> implements Loadable {
|
public final class NetworkLoadable<T> implements Loadable {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses an object from data loaded over the network.
|
||||||
|
*/
|
||||||
|
public interface Parser<T> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses an object from a network response.
|
||||||
|
*
|
||||||
|
* @param connectionUrl The source of the response, after any redirection.
|
||||||
|
* @param inputStream An {@link InputStream} from which the response data can be read.
|
||||||
|
* @param inputEncoding The encoding of the data, if available.
|
||||||
|
* @return The parsed object.
|
||||||
|
* @throws ParserException If an error occurs parsing the data.
|
||||||
|
* @throws IOException If an error occurs reading data from the stream.
|
||||||
|
*/
|
||||||
|
T parse(String connectionUrl, InputStream inputStream, String inputEncoding)
|
||||||
|
throws ParserException, IOException;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public static final int DEFAULT_TIMEOUT_MILLIS = 10000;
|
public static final int DEFAULT_TIMEOUT_MILLIS = 10000;
|
||||||
|
|
||||||
private final String url;
|
private final String url;
|
||||||
private final String userAgent;
|
private final String userAgent;
|
||||||
private final int timeoutMillis;
|
private final int timeoutMillis;
|
||||||
|
private final Parser<T> parser;
|
||||||
|
|
||||||
private volatile T result;
|
private volatile T result;
|
||||||
private volatile boolean isCanceled;
|
private volatile boolean isCanceled;
|
||||||
@ -42,20 +63,23 @@ public abstract class NetworkLoadable<T> implements Loadable {
|
|||||||
/**
|
/**
|
||||||
* @param url The url from which the object should be loaded.
|
* @param url The url from which the object should be loaded.
|
||||||
* @param userAgent The user agent to use when requesting the object.
|
* @param userAgent The user agent to use when requesting the object.
|
||||||
|
* @param parser Parses the object from the network response.
|
||||||
*/
|
*/
|
||||||
public NetworkLoadable(String url, String userAgent) {
|
public NetworkLoadable(String url, String userAgent, Parser<T> parser) {
|
||||||
this(url, userAgent, DEFAULT_TIMEOUT_MILLIS);
|
this(url, userAgent, DEFAULT_TIMEOUT_MILLIS, parser);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param url The url from which the object should be loaded.
|
* @param url The url from which the object should be loaded.
|
||||||
* @param userAgent The user agent to use when requesting the object.
|
* @param userAgent The user agent to use when requesting the object.
|
||||||
* @param timeoutMillis The desired http timeout in milliseconds.
|
* @param timeoutMillis The desired http timeout in milliseconds.
|
||||||
|
* @param parser Parses the object from the network response.
|
||||||
*/
|
*/
|
||||||
public NetworkLoadable(String url, String userAgent, int timeoutMillis) {
|
public NetworkLoadable(String url, String userAgent, int timeoutMillis, Parser<T> parser) {
|
||||||
this.url = url;
|
this.url = url;
|
||||||
this.userAgent = userAgent;
|
this.userAgent = userAgent;
|
||||||
this.timeoutMillis = timeoutMillis;
|
this.timeoutMillis = timeoutMillis;
|
||||||
|
this.parser = parser;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -85,7 +109,7 @@ public abstract class NetworkLoadable<T> implements Loadable {
|
|||||||
URLConnection connection = configureConnection(new URL(url));
|
URLConnection connection = configureConnection(new URL(url));
|
||||||
inputStream = connection.getInputStream();
|
inputStream = connection.getInputStream();
|
||||||
inputEncoding = connection.getContentEncoding();
|
inputEncoding = connection.getContentEncoding();
|
||||||
result = parse(connection.getURL().toString(), inputStream, inputEncoding);
|
result = parser.parse(connection.getURL().toString(), inputStream, inputEncoding);
|
||||||
} finally {
|
} finally {
|
||||||
if (inputStream != null) {
|
if (inputStream != null) {
|
||||||
inputStream.close();
|
inputStream.close();
|
||||||
@ -93,19 +117,6 @@ public abstract class NetworkLoadable<T> implements Loadable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Parses the raw data into an object.
|
|
||||||
*
|
|
||||||
* @param connectionUrl The url, after any redirection has taken place.
|
|
||||||
* @param inputStream An {@link InputStream} from which the raw data can be read.
|
|
||||||
* @param inputEncoding The encoding of the raw data, if available.
|
|
||||||
* @return The parsed object.
|
|
||||||
* @throws ParserException If an error occurs parsing the data.
|
|
||||||
* @throws IOException If an error occurs reading data from the stream.
|
|
||||||
*/
|
|
||||||
protected abstract T parse(String connectionUrl, InputStream inputStream, String inputEncoding)
|
|
||||||
throws ParserException, IOException;
|
|
||||||
|
|
||||||
private URLConnection configureConnection(URL url) throws IOException {
|
private URLConnection configureConnection(URL url) throws IOException {
|
||||||
URLConnection connection = url.openConnection();
|
URLConnection connection = url.openConnection();
|
||||||
connection.setConnectTimeout(timeoutMillis);
|
connection.setConnectTimeout(timeoutMillis);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user