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.Allocator;
|
||||||
import com.google.android.exoplayer2.upstream.DataSource;
|
import com.google.android.exoplayer2.upstream.DataSource;
|
||||||
import com.google.android.exoplayer2.upstream.Loader;
|
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.upstream.ParsingLoadable;
|
||||||
import com.google.android.exoplayer2.util.Assertions;
|
import com.google.android.exoplayer2.util.Assertions;
|
||||||
import com.google.android.exoplayer2.util.Util;
|
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 static final long MIN_LIVE_DEFAULT_START_POSITION_US = 5000000;
|
||||||
|
|
||||||
private final Uri manifestUri;
|
private final Uri manifestUri;
|
||||||
private final DataSource.Factory dataSourceFactory;
|
private final DataSource.Factory manifestDataSourceFactory;
|
||||||
private final SsChunkSource.Factory chunkSourceFactory;
|
private final SsChunkSource.Factory chunkSourceFactory;
|
||||||
private final int minLoadableRetryCount;
|
private final int minLoadableRetryCount;
|
||||||
private final long livePresentationDelayMs;
|
private final long livePresentationDelayMs;
|
||||||
@ -75,12 +76,54 @@ public final class SsMediaSource implements MediaSource,
|
|||||||
private MediaSource.Listener sourceListener;
|
private MediaSource.Listener sourceListener;
|
||||||
private DataSource manifestDataSource;
|
private DataSource manifestDataSource;
|
||||||
private Loader manifestLoader;
|
private Loader manifestLoader;
|
||||||
|
private LoaderErrorThrower manifestLoaderErrorThrower;
|
||||||
|
|
||||||
private long manifestLoadStartTimestamp;
|
private long manifestLoadStartTimestamp;
|
||||||
private SsManifest manifest;
|
private SsManifest manifest;
|
||||||
|
|
||||||
private Handler manifestRefreshHandler;
|
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,
|
public SsMediaSource(Uri manifestUri, DataSource.Factory manifestDataSourceFactory,
|
||||||
SsChunkSource.Factory chunkSourceFactory, Handler eventHandler,
|
SsChunkSource.Factory chunkSourceFactory, Handler eventHandler,
|
||||||
AdaptiveMediaSourceEventListener eventListener) {
|
AdaptiveMediaSourceEventListener eventListener) {
|
||||||
@ -89,18 +132,67 @@ public final class SsMediaSource implements MediaSource,
|
|||||||
eventListener);
|
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,
|
SsChunkSource.Factory chunkSourceFactory, int minLoadableRetryCount,
|
||||||
long livePresentationDelayMs, Handler eventHandler,
|
long livePresentationDelayMs, Handler eventHandler,
|
||||||
AdaptiveMediaSourceEventListener eventListener) {
|
AdaptiveMediaSourceEventListener eventListener) {
|
||||||
this.manifestUri = Util.toLowerInvariant(manifestUri.getLastPathSegment()).equals("manifest")
|
this(manifestUri, manifestDataSourceFactory, new SsManifestParser(), chunkSourceFactory,
|
||||||
? manifestUri : Uri.withAppendedPath(manifestUri, "Manifest");
|
minLoadableRetryCount, livePresentationDelayMs, eventHandler, eventListener);
|
||||||
this.dataSourceFactory = 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 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.chunkSourceFactory = chunkSourceFactory;
|
||||||
this.minLoadableRetryCount = minLoadableRetryCount;
|
this.minLoadableRetryCount = minLoadableRetryCount;
|
||||||
this.livePresentationDelayMs = livePresentationDelayMs;
|
this.livePresentationDelayMs = livePresentationDelayMs;
|
||||||
this.eventDispatcher = new EventDispatcher(eventHandler, eventListener);
|
this.eventDispatcher = new EventDispatcher(eventHandler, eventListener);
|
||||||
manifestParser = new SsManifestParser();
|
|
||||||
mediaPeriods = new ArrayList<>();
|
mediaPeriods = new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -109,22 +201,28 @@ public final class SsMediaSource implements MediaSource,
|
|||||||
@Override
|
@Override
|
||||||
public void prepareSource(MediaSource.Listener listener) {
|
public void prepareSource(MediaSource.Listener listener) {
|
||||||
sourceListener = listener;
|
sourceListener = listener;
|
||||||
manifestDataSource = dataSourceFactory.createDataSource();
|
if (manifest != null) {
|
||||||
manifestLoader = new Loader("Loader:Manifest");
|
manifestLoaderErrorThrower = new LoaderErrorThrower.Dummy();
|
||||||
manifestRefreshHandler = new Handler();
|
processManifest();
|
||||||
startLoadingManifest();
|
} else {
|
||||||
|
manifestDataSource = manifestDataSourceFactory.createDataSource();
|
||||||
|
manifestLoader = new Loader("Loader:Manifest");
|
||||||
|
manifestLoaderErrorThrower = manifestLoader;
|
||||||
|
manifestRefreshHandler = new Handler();
|
||||||
|
startLoadingManifest();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void maybeThrowSourceInfoRefreshError() throws IOException {
|
public void maybeThrowSourceInfoRefreshError() throws IOException {
|
||||||
manifestLoader.maybeThrowError();
|
manifestLoaderErrorThrower.maybeThrowError();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MediaPeriod createPeriod(int index, Allocator allocator, long positionUs) {
|
public MediaPeriod createPeriod(int index, Allocator allocator, long positionUs) {
|
||||||
Assertions.checkArgument(index == 0);
|
Assertions.checkArgument(index == 0);
|
||||||
SsMediaPeriod period = new SsMediaPeriod(manifest, chunkSourceFactory, minLoadableRetryCount,
|
SsMediaPeriod period = new SsMediaPeriod(manifest, chunkSourceFactory, minLoadableRetryCount,
|
||||||
eventDispatcher, manifestLoader, allocator);
|
eventDispatcher, manifestLoaderErrorThrower, allocator);
|
||||||
mediaPeriods.add(period);
|
mediaPeriods.add(period);
|
||||||
return period;
|
return period;
|
||||||
}
|
}
|
||||||
@ -160,6 +258,29 @@ public final class SsMediaSource implements MediaSource,
|
|||||||
loadDurationMs, loadable.bytesLoaded());
|
loadDurationMs, loadable.bytesLoaded());
|
||||||
manifest = loadable.getResult();
|
manifest = loadable.getResult();
|
||||||
manifestLoadStartTimestamp = elapsedRealtimeMs - loadDurationMs;
|
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++) {
|
for (int i = 0; i < mediaPeriods.size(); i++) {
|
||||||
mediaPeriods.get(i).updateManifest(manifest);
|
mediaPeriods.get(i).updateManifest(manifest);
|
||||||
}
|
}
|
||||||
@ -198,27 +319,8 @@ public final class SsMediaSource implements MediaSource,
|
|||||||
timeline = new SinglePeriodTimeline(manifest.durationUs, isSeekable);
|
timeline = new SinglePeriodTimeline(manifest.durationUs, isSeekable);
|
||||||
}
|
}
|
||||||
sourceListener.onSourceInfoRefreshed(timeline, manifest);
|
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() {
|
private void scheduleManifestRefresh() {
|
||||||
if (!manifest.isLive) {
|
if (!manifest.isLive) {
|
||||||
return;
|
return;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user