diff --git a/demos/cast/src/main/java/com/google/android/exoplayer2/castdemo/PlayerManager.java b/demos/cast/src/main/java/com/google/android/exoplayer2/castdemo/PlayerManager.java index 0f4adfae99..7d63150201 100644 --- a/demos/cast/src/main/java/com/google/android/exoplayer2/castdemo/PlayerManager.java +++ b/demos/cast/src/main/java/com/google/android/exoplayer2/castdemo/PlayerManager.java @@ -393,10 +393,9 @@ import java.util.ArrayList; new DefaultSsChunkSource.Factory(DATA_SOURCE_FACTORY)) .build(); case DemoUtil.MIME_TYPE_DASH: - return DashMediaSource.Builder - .forManifestUri(uri, DATA_SOURCE_FACTORY, - new DefaultDashChunkSource.Factory(DATA_SOURCE_FACTORY)) - .build(); + return new DashMediaSource.Factory( + new DefaultDashChunkSource.Factory(DATA_SOURCE_FACTORY), DATA_SOURCE_FACTORY) + .createMediaSource(uri); case DemoUtil.MIME_TYPE_HLS: return HlsMediaSource.Builder .forDataSource(uri, DATA_SOURCE_FACTORY) diff --git a/demos/main/src/main/java/com/google/android/exoplayer2/demo/PlayerActivity.java b/demos/main/src/main/java/com/google/android/exoplayer2/demo/PlayerActivity.java index 0623f48a51..1be6df8437 100644 --- a/demos/main/src/main/java/com/google/android/exoplayer2/demo/PlayerActivity.java +++ b/demos/main/src/main/java/com/google/android/exoplayer2/demo/PlayerActivity.java @@ -371,11 +371,10 @@ public class PlayerActivity extends Activity implements OnClickListener, .setEventListener(mainHandler, eventLogger) .build(); case C.TYPE_DASH: - return DashMediaSource.Builder - .forManifestUri(uri, buildDataSourceFactory(false), - new DefaultDashChunkSource.Factory(mediaDataSourceFactory)) - .setEventListener(mainHandler, eventLogger) - .build(); + return new DashMediaSource.Factory( + new DefaultDashChunkSource.Factory(mediaDataSourceFactory), + buildDataSourceFactory(false)) + .createMediaSource(uri, mainHandler, eventLogger); case C.TYPE_HLS: return HlsMediaSource.Builder .forDataSource(uri, mediaDataSourceFactory) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/ads/AdsMediaSource.java b/library/core/src/main/java/com/google/android/exoplayer2/source/ads/AdsMediaSource.java index 54a8fd96ae..c701d6ca64 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/source/ads/AdsMediaSource.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/ads/AdsMediaSource.java @@ -44,6 +44,31 @@ import java.util.Map; */ public final class AdsMediaSource implements MediaSource { + /** Factory for creating {@link MediaSource}s to play ad media. */ + public interface MediaSourceFactory { + + /** + * Creates a new {@link MediaSource} for loading the ad media with the specified {@code uri}. + * + * @param uri The URI of the media or manifest to play. + * @param handler A handler for listener events. May be null if delivery of events is not + * required. + * @param listener A listener for events. May be null if delivery of events is not required. + * @return The new media source. + */ + MediaSource createMediaSource( + Uri uri, @Nullable Handler handler, @Nullable MediaSourceEventListener listener); + + /** + * Returns the content types supported by media sources created by this factory. Each element + * should be one of {@link C#TYPE_DASH}, {@link C#TYPE_SS}, {@link C#TYPE_HLS} or {@link + * C#TYPE_OTHER}. + * + * @return The content types supported by media sources created by this factory. + */ + int[] getSupportedTypes(); + } + /** Listener for ads media source events. */ public interface EventListener extends MediaSourceEventListener { @@ -77,7 +102,7 @@ public final class AdsMediaSource implements MediaSource { @Nullable private final EventListener eventListener; private final Handler mainHandler; private final ComponentListener componentListener; - private final AdMediaSourceFactory adMediaSourceFactory; + private final MediaSourceFactory adMediaSourceFactory; private final Map> deferredMediaPeriodByAdMediaSource; private final Timeline.Period period; @@ -138,7 +163,7 @@ public final class AdsMediaSource implements MediaSource { this.eventListener = eventListener; mainHandler = new Handler(Looper.getMainLooper()); componentListener = new ComponentListener(); - adMediaSourceFactory = new ExtractorAdMediaSourceFactory(dataSourceFactory); + adMediaSourceFactory = new ExtractorMediaSourceFactory(dataSourceFactory); deferredMediaPeriodByAdMediaSource = new HashMap<>(); period = new Timeline.Period(); adGroupMediaSources = new MediaSource[0][]; @@ -186,7 +211,7 @@ public final class AdsMediaSource implements MediaSource { if (adGroupMediaSources[adGroupIndex].length <= adIndexInAdGroup) { Uri adUri = adPlaybackState.adUris[id.adGroupIndex][id.adIndexInAdGroup]; final MediaSource adMediaSource = - adMediaSourceFactory.createAdMediaSource(adUri, eventHandler, eventListener); + adMediaSourceFactory.createMediaSource(adUri, eventHandler, eventListener); int oldAdCount = adGroupMediaSources[id.adGroupIndex].length; if (adIndexInAdGroup >= oldAdCount) { int adCount = adIndexInAdGroup + 1; @@ -371,44 +396,16 @@ public final class AdsMediaSource implements MediaSource { } - /** - * Factory for {@link MediaSource}s for loading ad media. - */ - private interface AdMediaSourceFactory { - - /** - * Creates a new {@link MediaSource} for loading the ad media with the specified {@code uri}. - * - * @param uri The URI of the ad. - * @param handler A handler for listener events. May be null if delivery of events is not - * required. - * @param listener A listener for events. May be null if delivery of events is not required. - * @return The new media source. - */ - MediaSource createAdMediaSource( - Uri uri, @Nullable Handler handler, @Nullable MediaSourceEventListener listener); - - /** - * Returns the content types supported by media sources created by this factory. Each element - * should be one of {@link C#TYPE_DASH}, {@link C#TYPE_SS}, {@link C#TYPE_HLS} or - * {@link C#TYPE_OTHER}. - * - * @return The content types supported by the factory. - */ - int[] getSupportedTypes(); - - } - - private static final class ExtractorAdMediaSourceFactory implements AdMediaSourceFactory { + private static final class ExtractorMediaSourceFactory implements MediaSourceFactory { private final DataSource.Factory dataSourceFactory; - public ExtractorAdMediaSourceFactory(DataSource.Factory dataSourceFactory) { + public ExtractorMediaSourceFactory(DataSource.Factory dataSourceFactory) { this.dataSourceFactory = dataSourceFactory; } @Override - public MediaSource createAdMediaSource( + public MediaSource createMediaSource( Uri uri, @Nullable Handler handler, @Nullable MediaSourceEventListener listener) { return new ExtractorMediaSource.Builder(uri, dataSourceFactory) .setEventListener(handler, listener) diff --git a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaSource.java b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaSource.java index af1a445b9f..9c0c58c87b 100644 --- a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaSource.java +++ b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaSource.java @@ -34,6 +34,7 @@ import com.google.android.exoplayer2.source.MediaSource; import com.google.android.exoplayer2.source.MediaSourceEventListener; import com.google.android.exoplayer2.source.MediaSourceEventListener.EventDispatcher; import com.google.android.exoplayer2.source.SequenceableLoader; +import com.google.android.exoplayer2.source.ads.AdsMediaSource; import com.google.android.exoplayer2.source.dash.manifest.DashManifest; import com.google.android.exoplayer2.source.dash.manifest.DashManifestParser; import com.google.android.exoplayer2.source.dash.manifest.UtcTimingElement; @@ -64,62 +65,35 @@ public final class DashMediaSource implements MediaSource { ExoPlayerLibraryInfo.registerModule("goog.exo.dash"); } - /** - * Builder for {@link DashMediaSource}. Each builder instance can only be used once. - */ - public static final class Builder { + /** Factory for {@link DashMediaSource}s. */ + public static final class Factory implements AdsMediaSource.MediaSourceFactory { - private final DashManifest manifest; - private final Uri manifestUri; - private final DataSource.Factory manifestDataSourceFactory; private final DashChunkSource.Factory chunkSourceFactory; + private final @Nullable DataSource.Factory manifestDataSourceFactory; - private ParsingLoadable.Parser manifestParser; - private MediaSourceEventListener eventListener; - private Handler eventHandler; + private @Nullable ParsingLoadable.Parser manifestParser; private CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory; - private int minLoadableRetryCount; private long livePresentationDelayMs; - private boolean isBuildCalled; + private boolean isCreateCalled; /** - * Creates a {@link Builder} for a {@link DashMediaSource} with a side-loaded manifest. + * Creates a new factory for {@link DashMediaSource}s. * - * @param manifest The manifest. {@link DashManifest#dynamic} must be false. * @param chunkSourceFactory A factory for {@link DashChunkSource} instances. - * @return A new builder. - */ - public static Builder forSideloadedManifest(DashManifest manifest, - DashChunkSource.Factory chunkSourceFactory) { - Assertions.checkArgument(!manifest.dynamic); - return new Builder(manifest, null, null, chunkSourceFactory); - } - - /** - * Creates a {@link Builder} for a {@link DashMediaSource} with a loadable manifest Uri. - * - * @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 DashChunkSource} instances. - * @return A new builder. + * to load (and refresh) the manifest. May be {@code null} if the factory will only ever be + * used to create create media sources with sideloaded manifests via {@link + * #createMediaSource(DashManifest, Handler, MediaSourceEventListener)}. */ - public static Builder forManifestUri(Uri manifestUri, - DataSource.Factory manifestDataSourceFactory, DashChunkSource.Factory chunkSourceFactory) { - return new Builder(null, manifestUri, manifestDataSourceFactory, chunkSourceFactory); - } - - private Builder(@Nullable DashManifest manifest, @Nullable Uri manifestUri, - @Nullable DataSource.Factory manifestDataSourceFactory, - DashChunkSource.Factory chunkSourceFactory) { - this.manifest = manifest; - this.manifestUri = manifestUri; + public Factory( + DashChunkSource.Factory chunkSourceFactory, + @Nullable DataSource.Factory manifestDataSourceFactory) { + this.chunkSourceFactory = Assertions.checkNotNull(chunkSourceFactory); this.manifestDataSourceFactory = manifestDataSourceFactory; - this.chunkSourceFactory = chunkSourceFactory; - minLoadableRetryCount = DEFAULT_MIN_LOADABLE_RETRY_COUNT; livePresentationDelayMs = DEFAULT_LIVE_PRESENTATION_DELAY_PREFER_MANIFEST_MS; + compositeSequenceableLoaderFactory = new DefaultCompositeSequenceableLoaderFactory(); } /** @@ -127,95 +101,140 @@ public final class DashMediaSource implements MediaSource { * {@link #DEFAULT_MIN_LOADABLE_RETRY_COUNT}. * * @param minLoadableRetryCount The minimum number of times to retry if a loading error occurs. - * @return This builder. + * @return This factory, for convenience. + * @throws IllegalStateException If one of the {@code create} methods has already been called. */ - public Builder setMinLoadableRetryCount(int minLoadableRetryCount) { + public Factory setMinLoadableRetryCount(int minLoadableRetryCount) { + Assertions.checkState(!isCreateCalled); this.minLoadableRetryCount = minLoadableRetryCount; return this; } /** * Sets the duration in milliseconds by which the default start position should precede the end - * of the live window for live playbacks. The default value is - * {@link #DEFAULT_LIVE_PRESENTATION_DELAY_PREFER_MANIFEST_MS}. + * of the live window for live playbacks. The default value is {@link + * #DEFAULT_LIVE_PRESENTATION_DELAY_PREFER_MANIFEST_MS}. * * @param livePresentationDelayMs For live playbacks, the duration in milliseconds by which the - * default start position should precede the end of the live window. Use - * {@link #DEFAULT_LIVE_PRESENTATION_DELAY_PREFER_MANIFEST_MS} to use the value specified by - * the manifest, if present. - * @return This builder. + * default start position should precede the end of the live window. Use {@link + * #DEFAULT_LIVE_PRESENTATION_DELAY_PREFER_MANIFEST_MS} to use the value specified by the + * manifest, if present. + * @return This factory, for convenience. + * @throws IllegalStateException If one of the {@code create} methods has already been called. */ - public Builder setLivePresentationDelayMs(long livePresentationDelayMs) { + public Factory setLivePresentationDelayMs(long livePresentationDelayMs) { + Assertions.checkState(!isCreateCalled); this.livePresentationDelayMs = livePresentationDelayMs; return this; } /** - * Sets the listener to respond to adaptive {@link MediaSource} events and the handler to - * deliver these events. - * - * @param eventHandler A handler for events. - * @param eventListener A listener of events. - * @return This builder. - */ - public Builder setEventListener(Handler eventHandler, MediaSourceEventListener eventListener) { - this.eventHandler = eventHandler; - this.eventListener = eventListener; - return this; - } - - /** - * Sets the manifest parser to parse loaded manifest data. The default is - * {@link DashManifestParser}, or {@code null} if the manifest is sideloaded. + * Sets the manifest parser to parse loaded manifest data when loading a manifest URI. * * @param manifestParser A parser for loaded manifest data. - * @return This builder. + * @return This factory, for convenience. + * @throws IllegalStateException If one of the {@code create} methods has already been called. */ - public Builder setManifestParser( + public Factory setManifestParser( ParsingLoadable.Parser manifestParser) { - this.manifestParser = manifestParser; + Assertions.checkState(!isCreateCalled); + this.manifestParser = Assertions.checkNotNull(manifestParser); return this; } /** * Sets the factory to create composite {@link SequenceableLoader}s for when this media source - * loads data from multiple streams (video, audio etc...). The default is an instance of - * {@link DefaultCompositeSequenceableLoaderFactory}. + * loads data from multiple streams (video, audio etc...). The default is an instance of {@link + * DefaultCompositeSequenceableLoaderFactory}. * - * @param compositeSequenceableLoaderFactory A factory to create composite - * {@link SequenceableLoader}s for when this media source loads data from multiple streams - * (video, audio etc...). - * @return This builder. + * @param compositeSequenceableLoaderFactory A factory to create composite {@link + * SequenceableLoader}s for when this media source loads data from multiple streams (video, + * audio etc...). + * @return This factory, for convenience. + * @throws IllegalStateException If one of the {@code create} methods has already been called. */ - public Builder setCompositeSequenceableLoaderFactory( + public Factory setCompositeSequenceableLoaderFactory( CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory) { - this.compositeSequenceableLoaderFactory = compositeSequenceableLoaderFactory; + Assertions.checkState(!isCreateCalled); + this.compositeSequenceableLoaderFactory = + Assertions.checkNotNull(compositeSequenceableLoaderFactory); return this; } /** - * Builds a new {@link DashMediaSource} using the current parameters. - *

- * After this call, the builder should not be re-used. + * Returns a new {@link DashMediaSource} using the current parameters and the specified + * sideloaded manifest. * - * @return The newly built {@link DashMediaSource}. + * @param manifest The manifest. {@link DashManifest#dynamic} must be false. + * @param eventHandler A handler for events. + * @param eventListener A listener of events. + * @return The new {@link DashMediaSource}. + * @throws IllegalArgumentException If {@link DashManifest#dynamic} is true. */ - public DashMediaSource build() { - Assertions.checkArgument((eventListener == null) == (eventHandler == null)); - Assertions.checkState(!isBuildCalled); - isBuildCalled = true; - boolean loadableManifestUri = manifestUri != null; - if (loadableManifestUri && manifestParser == null) { - manifestParser = new DashManifestParser(); - } - if (compositeSequenceableLoaderFactory == null) { - compositeSequenceableLoaderFactory = new DefaultCompositeSequenceableLoaderFactory(); - } - return new DashMediaSource(manifest, manifestUri, manifestDataSourceFactory, manifestParser, - chunkSourceFactory, compositeSequenceableLoaderFactory, minLoadableRetryCount, - livePresentationDelayMs, eventHandler, eventListener); + public DashMediaSource createMediaSource( + DashManifest manifest, + @Nullable Handler eventHandler, + @Nullable MediaSourceEventListener eventListener) { + Assertions.checkArgument(!manifest.dynamic); + isCreateCalled = true; + return new DashMediaSource( + manifest, + null, + null, + null, + chunkSourceFactory, + compositeSequenceableLoaderFactory, + minLoadableRetryCount, + livePresentationDelayMs, + eventHandler, + eventListener); } + /** + * Returns a new {@link DashMediaSource} using the current parameters. Media source events will + * not be delivered. + * + * @param manifestUri The manifest {@link Uri}. + * @return The new {@link DashMediaSource}. + */ + public DashMediaSource createMediaSource(Uri manifestUri) { + return createMediaSource(manifestUri, null, null); + } + + /** + * Returns a new {@link DashMediaSource} using the current parameters. + * + * @param manifestUri The manifest {@link Uri}. + * @param eventHandler A handler for events. + * @param eventListener A listener of events. + * @return The new {@link DashMediaSource}. + */ + @Override + public DashMediaSource createMediaSource( + Uri manifestUri, + @Nullable Handler eventHandler, + @Nullable MediaSourceEventListener eventListener) { + isCreateCalled = true; + if (manifestParser == null) { + manifestParser = new DashManifestParser(); + } + return new DashMediaSource( + null, + Assertions.checkNotNull(manifestUri), + manifestDataSourceFactory, + manifestParser, + chunkSourceFactory, + compositeSequenceableLoaderFactory, + minLoadableRetryCount, + livePresentationDelayMs, + eventHandler, + eventListener); + } + + @Override + public int[] getSupportedTypes() { + return new int[] {C.TYPE_DASH}; + } } /** @@ -283,7 +302,7 @@ public final class DashMediaSource implements MediaSource { * @param chunkSourceFactory A factory for {@link DashChunkSource} 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. - * @deprecated Use {@link Builder} instead. + * @deprecated Use {@link Factory} instead. */ @Deprecated public DashMediaSource( @@ -303,7 +322,7 @@ public final class DashMediaSource implements MediaSource { * @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. - * @deprecated Use {@link Builder} instead. + * @deprecated Use {@link Factory} instead. */ @Deprecated public DashMediaSource( @@ -327,7 +346,7 @@ public final class DashMediaSource implements MediaSource { * @param chunkSourceFactory A factory for {@link DashChunkSource} 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. - * @deprecated Use {@link Builder} instead. + * @deprecated Use {@link Factory} instead. */ @Deprecated public DashMediaSource( @@ -356,7 +375,7 @@ public final class DashMediaSource implements MediaSource { * manifest, if present. * @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. - * @deprecated Use {@link Builder} instead. + * @deprecated Use {@link Factory} instead. */ @Deprecated public DashMediaSource( @@ -387,7 +406,7 @@ public final class DashMediaSource implements MediaSource { * manifest, if present. * @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. - * @deprecated Use {@link Builder} instead. + * @deprecated Use {@link Factory} instead. */ @Deprecated public DashMediaSource( diff --git a/playbacktests/src/androidTest/java/com/google/android/exoplayer2/playbacktests/gts/DashTestRunner.java b/playbacktests/src/androidTest/java/com/google/android/exoplayer2/playbacktests/gts/DashTestRunner.java index 215d8a0518..8973853245 100644 --- a/playbacktests/src/androidTest/java/com/google/android/exoplayer2/playbacktests/gts/DashTestRunner.java +++ b/playbacktests/src/androidTest/java/com/google/android/exoplayer2/playbacktests/gts/DashTestRunner.java @@ -316,11 +316,10 @@ public final class DashTestRunner { Uri manifestUri = Uri.parse(manifestUrl); DefaultDashChunkSource.Factory chunkSourceFactory = new DefaultDashChunkSource.Factory( mediaDataSourceFactory); - return DashMediaSource.Builder - .forManifestUri(manifestUri, manifestDataSourceFactory, chunkSourceFactory) + return new DashMediaSource.Factory(chunkSourceFactory, manifestDataSourceFactory) .setMinLoadableRetryCount(MIN_LOADABLE_RETRY_COUNT) .setLivePresentationDelayMs(0) - .build(); + .createMediaSource(manifestUri); } @Override