Allow SsMediaSource to take an optional SsManifest
Also allow custom SsManifestParser injection. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=139813660
This commit is contained in:
parent
81ce6bba7a
commit
42fadfe083
@ -32,6 +32,7 @@ import com.google.android.exoplayer2.source.smoothstreaming.manifest.SsManifestP
|
||||
import com.google.android.exoplayer2.upstream.Allocator;
|
||||
import com.google.android.exoplayer2.upstream.DataSource;
|
||||
import com.google.android.exoplayer2.upstream.Loader;
|
||||
import com.google.android.exoplayer2.upstream.LoaderErrorThrower;
|
||||
import com.google.android.exoplayer2.upstream.ParsingLoadable;
|
||||
import com.google.android.exoplayer2.util.Assertions;
|
||||
import com.google.android.exoplayer2.util.Util;
|
||||
@ -64,7 +65,7 @@ public final class SsMediaSource implements MediaSource,
|
||||
private static final long MIN_LIVE_DEFAULT_START_POSITION_US = 5000000;
|
||||
|
||||
private final Uri manifestUri;
|
||||
private final DataSource.Factory dataSourceFactory;
|
||||
private final DataSource.Factory manifestDataSourceFactory;
|
||||
private final SsChunkSource.Factory chunkSourceFactory;
|
||||
private final int minLoadableRetryCount;
|
||||
private final long livePresentationDelayMs;
|
||||
@ -75,12 +76,54 @@ public final class SsMediaSource implements MediaSource,
|
||||
private MediaSource.Listener sourceListener;
|
||||
private DataSource manifestDataSource;
|
||||
private Loader manifestLoader;
|
||||
private LoaderErrorThrower manifestLoaderErrorThrower;
|
||||
|
||||
private long manifestLoadStartTimestamp;
|
||||
private SsManifest manifest;
|
||||
|
||||
private Handler manifestRefreshHandler;
|
||||
|
||||
/**
|
||||
* Constructs an instance to play a given {@link SsManifest}, which must not be live.
|
||||
*
|
||||
* @param manifest The manifest. {@link SsManifest#isLive} must be false.
|
||||
* @param chunkSourceFactory A factory for {@link SsChunkSource} instances.
|
||||
* @param eventHandler A handler for events. 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 SsMediaSource(SsManifest manifest, SsChunkSource.Factory chunkSourceFactory,
|
||||
Handler eventHandler, AdaptiveMediaSourceEventListener eventListener) {
|
||||
this(manifest, chunkSourceFactory, DEFAULT_MIN_LOADABLE_RETRY_COUNT,
|
||||
eventHandler, eventListener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an instance to play a given {@link SsManifest}, which must not be live.
|
||||
*
|
||||
* @param manifest The manifest. {@link SsManifest#isLive} must be false.
|
||||
* @param chunkSourceFactory A factory for {@link SsChunkSource} instances.
|
||||
* @param minLoadableRetryCount The minimum number of times to retry if a loading error occurs.
|
||||
* @param eventHandler A handler for events. 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 SsMediaSource(SsManifest manifest, SsChunkSource.Factory chunkSourceFactory,
|
||||
int minLoadableRetryCount, Handler eventHandler,
|
||||
AdaptiveMediaSourceEventListener eventListener) {
|
||||
this(manifest, null, null, null, chunkSourceFactory, minLoadableRetryCount,
|
||||
DEFAULT_LIVE_PRESENTATION_DELAY_MS, eventHandler, eventListener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an instance to play the manifest at a given {@link Uri}, which may be live or
|
||||
* on-demand.
|
||||
*
|
||||
* @param manifestUri The manifest {@link Uri}.
|
||||
* @param manifestDataSourceFactory A factory for {@link DataSource} instances that will be used
|
||||
* to load (and refresh) the manifest.
|
||||
* @param chunkSourceFactory A factory for {@link SsChunkSource} instances.
|
||||
* @param eventHandler A handler for events. 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 SsMediaSource(Uri manifestUri, DataSource.Factory manifestDataSourceFactory,
|
||||
SsChunkSource.Factory chunkSourceFactory, Handler eventHandler,
|
||||
AdaptiveMediaSourceEventListener eventListener) {
|
||||
@ -89,18 +132,67 @@ public final class SsMediaSource implements MediaSource,
|
||||
eventListener);
|
||||
}
|
||||
|
||||
public SsMediaSource(Uri manifestUri, DataSource.Factory dataSourceFactory,
|
||||
/**
|
||||
* Constructs an instance to play the manifest at a given {@link Uri}, which may be live or
|
||||
* on-demand.
|
||||
*
|
||||
* @param manifestUri The manifest {@link Uri}.
|
||||
* @param manifestDataSourceFactory A factory for {@link DataSource} instances that will be used
|
||||
* to load (and refresh) the manifest.
|
||||
* @param chunkSourceFactory A factory for {@link SsChunkSource} instances.
|
||||
* @param minLoadableRetryCount The minimum number of times to retry if a loading error occurs.
|
||||
* @param livePresentationDelayMs For live playbacks, the duration in milliseconds by which the
|
||||
* default start position should precede the end of the live window.
|
||||
* @param eventHandler A handler for events. 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 SsMediaSource(Uri manifestUri, DataSource.Factory manifestDataSourceFactory,
|
||||
SsChunkSource.Factory chunkSourceFactory, int minLoadableRetryCount,
|
||||
long livePresentationDelayMs, Handler eventHandler,
|
||||
AdaptiveMediaSourceEventListener eventListener) {
|
||||
this.manifestUri = Util.toLowerInvariant(manifestUri.getLastPathSegment()).equals("manifest")
|
||||
? manifestUri : Uri.withAppendedPath(manifestUri, "Manifest");
|
||||
this.dataSourceFactory = dataSourceFactory;
|
||||
this(manifestUri, manifestDataSourceFactory, new SsManifestParser(), chunkSourceFactory,
|
||||
minLoadableRetryCount, livePresentationDelayMs, eventHandler, eventListener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an instance to play the manifest at a given {@link Uri}, which may be live or
|
||||
* on-demand.
|
||||
*
|
||||
* @param manifestUri The manifest {@link Uri}.
|
||||
* @param manifestDataSourceFactory A factory for {@link DataSource} instances that will be used
|
||||
* to load (and refresh) the manifest.
|
||||
* @param manifestParser A parser for loaded manifest data.
|
||||
* @param chunkSourceFactory A factory for {@link SsChunkSource} instances.
|
||||
* @param minLoadableRetryCount The minimum number of times to retry if a loading error occurs.
|
||||
* @param livePresentationDelayMs For live playbacks, the duration in milliseconds by which the
|
||||
* default start position should precede the end of the live window.
|
||||
* @param eventHandler A handler for events. 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 SsMediaSource(Uri manifestUri, DataSource.Factory manifestDataSourceFactory,
|
||||
SsManifestParser manifestParser, SsChunkSource.Factory chunkSourceFactory,
|
||||
int minLoadableRetryCount, long livePresentationDelayMs, Handler eventHandler,
|
||||
AdaptiveMediaSourceEventListener eventListener) {
|
||||
this(null, manifestUri, manifestDataSourceFactory, manifestParser, chunkSourceFactory,
|
||||
minLoadableRetryCount, livePresentationDelayMs, eventHandler, eventListener);
|
||||
}
|
||||
|
||||
private SsMediaSource(SsManifest manifest, Uri manifestUri,
|
||||
DataSource.Factory manifestDataSourceFactory, SsManifestParser manifestParser,
|
||||
SsChunkSource.Factory chunkSourceFactory, int minLoadableRetryCount,
|
||||
long livePresentationDelayMs, Handler eventHandler,
|
||||
AdaptiveMediaSourceEventListener eventListener) {
|
||||
Assertions.checkState(manifest == null || !manifest.isLive);
|
||||
this.manifest = manifest;
|
||||
this.manifestUri = manifestUri == null ? null
|
||||
: Util.toLowerInvariant(manifestUri.getLastPathSegment()).equals("manifest") ? manifestUri
|
||||
: Uri.withAppendedPath(manifestUri, "Manifest");
|
||||
this.manifestDataSourceFactory = manifestDataSourceFactory;
|
||||
this.manifestParser = manifestParser;
|
||||
this.chunkSourceFactory = chunkSourceFactory;
|
||||
this.minLoadableRetryCount = minLoadableRetryCount;
|
||||
this.livePresentationDelayMs = livePresentationDelayMs;
|
||||
this.eventDispatcher = new EventDispatcher(eventHandler, eventListener);
|
||||
manifestParser = new SsManifestParser();
|
||||
mediaPeriods = new ArrayList<>();
|
||||
}
|
||||
|
||||
@ -109,22 +201,28 @@ public final class SsMediaSource implements MediaSource,
|
||||
@Override
|
||||
public void prepareSource(MediaSource.Listener listener) {
|
||||
sourceListener = listener;
|
||||
manifestDataSource = dataSourceFactory.createDataSource();
|
||||
if (manifest != null) {
|
||||
manifestLoaderErrorThrower = new LoaderErrorThrower.Dummy();
|
||||
processManifest();
|
||||
} else {
|
||||
manifestDataSource = manifestDataSourceFactory.createDataSource();
|
||||
manifestLoader = new Loader("Loader:Manifest");
|
||||
manifestLoaderErrorThrower = manifestLoader;
|
||||
manifestRefreshHandler = new Handler();
|
||||
startLoadingManifest();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void maybeThrowSourceInfoRefreshError() throws IOException {
|
||||
manifestLoader.maybeThrowError();
|
||||
manifestLoaderErrorThrower.maybeThrowError();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MediaPeriod createPeriod(int index, Allocator allocator, long positionUs) {
|
||||
Assertions.checkArgument(index == 0);
|
||||
SsMediaPeriod period = new SsMediaPeriod(manifest, chunkSourceFactory, minLoadableRetryCount,
|
||||
eventDispatcher, manifestLoader, allocator);
|
||||
eventDispatcher, manifestLoaderErrorThrower, allocator);
|
||||
mediaPeriods.add(period);
|
||||
return period;
|
||||
}
|
||||
@ -160,6 +258,29 @@ public final class SsMediaSource implements MediaSource,
|
||||
loadDurationMs, loadable.bytesLoaded());
|
||||
manifest = loadable.getResult();
|
||||
manifestLoadStartTimestamp = elapsedRealtimeMs - loadDurationMs;
|
||||
processManifest();
|
||||
scheduleManifestRefresh();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadCanceled(ParsingLoadable<SsManifest> loadable, long elapsedRealtimeMs,
|
||||
long loadDurationMs, boolean released) {
|
||||
eventDispatcher.loadCompleted(loadable.dataSpec, loadable.type, elapsedRealtimeMs,
|
||||
loadDurationMs, loadable.bytesLoaded());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int onLoadError(ParsingLoadable<SsManifest> loadable, long elapsedRealtimeMs,
|
||||
long loadDurationMs, IOException error) {
|
||||
boolean isFatal = error instanceof ParserException;
|
||||
eventDispatcher.loadError(loadable.dataSpec, loadable.type, elapsedRealtimeMs, loadDurationMs,
|
||||
loadable.bytesLoaded(), error, isFatal);
|
||||
return isFatal ? Loader.DONT_RETRY_FATAL : Loader.RETRY;
|
||||
}
|
||||
|
||||
// Internal methods
|
||||
|
||||
private void processManifest() {
|
||||
for (int i = 0; i < mediaPeriods.size(); i++) {
|
||||
mediaPeriods.get(i).updateManifest(manifest);
|
||||
}
|
||||
@ -198,27 +319,8 @@ public final class SsMediaSource implements MediaSource,
|
||||
timeline = new SinglePeriodTimeline(manifest.durationUs, isSeekable);
|
||||
}
|
||||
sourceListener.onSourceInfoRefreshed(timeline, manifest);
|
||||
scheduleManifestRefresh();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadCanceled(ParsingLoadable<SsManifest> loadable, long elapsedRealtimeMs,
|
||||
long loadDurationMs, boolean released) {
|
||||
eventDispatcher.loadCompleted(loadable.dataSpec, loadable.type, elapsedRealtimeMs,
|
||||
loadDurationMs, loadable.bytesLoaded());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int onLoadError(ParsingLoadable<SsManifest> loadable, long elapsedRealtimeMs,
|
||||
long loadDurationMs, IOException error) {
|
||||
boolean isFatal = error instanceof ParserException;
|
||||
eventDispatcher.loadError(loadable.dataSpec, loadable.type, elapsedRealtimeMs, loadDurationMs,
|
||||
loadable.bytesLoaded(), error, isFatal);
|
||||
return isFatal ? Loader.DONT_RETRY_FATAL : Loader.RETRY;
|
||||
}
|
||||
|
||||
// Internal methods
|
||||
|
||||
private void scheduleManifestRefresh() {
|
||||
if (!manifest.isLive) {
|
||||
return;
|
||||
|
Loading…
x
Reference in New Issue
Block a user