diff --git a/RELEASENOTES.md b/RELEASENOTES.md index d2d0105b55..2a5ccb583c 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -2,6 +2,8 @@ ### dev-v2 (not yet released) ### +* Add Builder to ExtractorMediaSource, HlsMediaSource, SsMediaSource, + DashMediaSource. * Support 32-bit PCM float output from `DefaultAudioSink`, and add an option to use this with `FfmpegAudioRenderer`. diff --git a/demos/ima/src/main/java/com/google/android/exoplayer2/imademo/PlayerManager.java b/demos/ima/src/main/java/com/google/android/exoplayer2/imademo/PlayerManager.java index e11c840d12..6b840830c5 100644 --- a/demos/ima/src/main/java/com/google/android/exoplayer2/imademo/PlayerManager.java +++ b/demos/ima/src/main/java/com/google/android/exoplayer2/imademo/PlayerManager.java @@ -21,8 +21,6 @@ import com.google.android.exoplayer2.ExoPlayer; import com.google.android.exoplayer2.ExoPlayerFactory; import com.google.android.exoplayer2.SimpleExoPlayer; import com.google.android.exoplayer2.ext.ima.ImaAdsLoader; -import com.google.android.exoplayer2.extractor.DefaultExtractorsFactory; -import com.google.android.exoplayer2.extractor.ExtractorsFactory; import com.google.android.exoplayer2.source.ExtractorMediaSource; import com.google.android.exoplayer2.source.MediaSource; import com.google.android.exoplayer2.source.ads.AdsMediaSource; @@ -69,13 +67,10 @@ import com.google.android.exoplayer2.util.Util; DataSource.Factory dataSourceFactory = new DefaultDataSourceFactory(context, Util.getUserAgent(context, context.getString(R.string.application_name))); - // Produces Extractor instances for parsing the content media (i.e. not the ad). - ExtractorsFactory extractorsFactory = new DefaultExtractorsFactory(); - // This is the MediaSource representing the content media (i.e. not the ad). String contentUrl = context.getString(R.string.content_url); - MediaSource contentMediaSource = new ExtractorMediaSource( - Uri.parse(contentUrl), dataSourceFactory, extractorsFactory, null, null); + MediaSource contentMediaSource = + new ExtractorMediaSource.Builder(Uri.parse(contentUrl), dataSourceFactory).build(); // Compose the content media source into a new AdsMediaSource with both ads and content. MediaSource mediaSourceWithAds = new AdsMediaSource(contentMediaSource, dataSourceFactory, 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 3d669c9477..ca253db809 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 @@ -46,7 +46,6 @@ import com.google.android.exoplayer2.drm.FrameworkMediaCrypto; import com.google.android.exoplayer2.drm.FrameworkMediaDrm; import com.google.android.exoplayer2.drm.HttpMediaDrmCallback; import com.google.android.exoplayer2.drm.UnsupportedDrmException; -import com.google.android.exoplayer2.extractor.DefaultExtractorsFactory; import com.google.android.exoplayer2.mediacodec.MediaCodecRenderer.DecoderInitializationException; import com.google.android.exoplayer2.mediacodec.MediaCodecUtil.DecoderQueryException; import com.google.android.exoplayer2.source.BehindLiveWindowException; @@ -379,8 +378,9 @@ public class PlayerActivity extends Activity implements OnClickListener, .setEventListener(mainHandler, eventLogger) .build(); case C.TYPE_OTHER: - return new ExtractorMediaSource(uri, mediaDataSourceFactory, new DefaultExtractorsFactory(), - mainHandler, eventLogger); + return new ExtractorMediaSource.Builder(uri, mediaDataSourceFactory) + .setEventListener(mainHandler, eventLogger) + .build(); default: { throw new IllegalStateException("Unsupported type: " + type); } diff --git a/extensions/flac/src/androidTest/java/com/google/android/exoplayer2/ext/flac/FlacPlaybackTest.java b/extensions/flac/src/androidTest/java/com/google/android/exoplayer2/ext/flac/FlacPlaybackTest.java index 65fb4c8195..bd6e698dc6 100644 --- a/extensions/flac/src/androidTest/java/com/google/android/exoplayer2/ext/flac/FlacPlaybackTest.java +++ b/extensions/flac/src/androidTest/java/com/google/android/exoplayer2/ext/flac/FlacPlaybackTest.java @@ -76,12 +76,10 @@ public class FlacPlaybackTest extends InstrumentationTestCase { DefaultTrackSelector trackSelector = new DefaultTrackSelector(); player = ExoPlayerFactory.newInstance(new Renderer[] {audioRenderer}, trackSelector); player.addListener(this); - ExtractorMediaSource mediaSource = new ExtractorMediaSource( - uri, - new DefaultDataSourceFactory(context, "ExoPlayerExtFlacTest"), - MatroskaExtractor.FACTORY, - null, - null); + ExtractorMediaSource mediaSource = new ExtractorMediaSource.Builder( + uri, new DefaultDataSourceFactory(context, "ExoPlayerExtFlacTest")) + .setExtractorsFactory(MatroskaExtractor.FACTORY) + .build(); player.prepare(mediaSource); player.setPlayWhenReady(true); Looper.loop(); diff --git a/extensions/opus/src/androidTest/java/com/google/android/exoplayer2/ext/opus/OpusPlaybackTest.java b/extensions/opus/src/androidTest/java/com/google/android/exoplayer2/ext/opus/OpusPlaybackTest.java index 591f43f38a..aa61df74d9 100644 --- a/extensions/opus/src/androidTest/java/com/google/android/exoplayer2/ext/opus/OpusPlaybackTest.java +++ b/extensions/opus/src/androidTest/java/com/google/android/exoplayer2/ext/opus/OpusPlaybackTest.java @@ -76,12 +76,10 @@ public class OpusPlaybackTest extends InstrumentationTestCase { DefaultTrackSelector trackSelector = new DefaultTrackSelector(); player = ExoPlayerFactory.newInstance(new Renderer[] {audioRenderer}, trackSelector); player.addListener(this); - ExtractorMediaSource mediaSource = new ExtractorMediaSource( - uri, - new DefaultDataSourceFactory(context, "ExoPlayerExtOpusTest"), - MatroskaExtractor.FACTORY, - null, - null); + ExtractorMediaSource mediaSource = new ExtractorMediaSource.Builder( + uri, new DefaultDataSourceFactory(context, "ExoPlayerExtOpusTest")) + .setExtractorsFactory(MatroskaExtractor.FACTORY) + .build(); player.prepare(mediaSource); player.setPlayWhenReady(true); Looper.loop(); diff --git a/extensions/vp9/src/androidTest/java/com/google/android/exoplayer2/ext/vp9/VpxPlaybackTest.java b/extensions/vp9/src/androidTest/java/com/google/android/exoplayer2/ext/vp9/VpxPlaybackTest.java index c2c1867a90..746f3d273f 100644 --- a/extensions/vp9/src/androidTest/java/com/google/android/exoplayer2/ext/vp9/VpxPlaybackTest.java +++ b/extensions/vp9/src/androidTest/java/com/google/android/exoplayer2/ext/vp9/VpxPlaybackTest.java @@ -105,12 +105,10 @@ public class VpxPlaybackTest extends InstrumentationTestCase { DefaultTrackSelector trackSelector = new DefaultTrackSelector(); player = ExoPlayerFactory.newInstance(new Renderer[] {videoRenderer}, trackSelector); player.addListener(this); - ExtractorMediaSource mediaSource = new ExtractorMediaSource( - uri, - new DefaultDataSourceFactory(context, "ExoPlayerExtVp9Test"), - MatroskaExtractor.FACTORY, - null, - null); + ExtractorMediaSource mediaSource = new ExtractorMediaSource.Builder( + uri, new DefaultDataSourceFactory(context, "ExoPlayerExtVp9Test")) + .setExtractorsFactory(MatroskaExtractor.FACTORY) + .build(); player.sendMessages(new ExoPlayer.ExoPlayerMessage(videoRenderer, LibvpxVideoRenderer.MSG_SET_OUTPUT_BUFFER_RENDERER, new VpxVideoSurfaceView(context))); diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/ExtractorMediaSource.java b/library/core/src/main/java/com/google/android/exoplayer2/source/ExtractorMediaSource.java index 1b3f6cb95c..066953b998 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/source/ExtractorMediaSource.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/ExtractorMediaSource.java @@ -98,6 +98,123 @@ public final class ExtractorMediaSource implements MediaSource, ExtractorMediaPe private long timelineDurationUs; private boolean timelineIsSeekable; + /** + * Builder for {@link ExtractorMediaSource}. Each builder instance can only be used once. + */ + public static final class Builder { + + private final Uri uri; + private final DataSource.Factory dataSourceFactory; + + private ExtractorsFactory extractorsFactory; + private int minLoadableRetryCount; + private Handler eventHandler; + private EventListener eventListener; + private String customCacheKey; + private int continueLoadingCheckIntervalBytes; + private boolean isBuildCalled; + + /** + * @param uri The {@link Uri} of the media stream. + * @param dataSourceFactory A factory for {@link DataSource}s to read the media. + */ + public Builder(Uri uri, DataSource.Factory dataSourceFactory) { + this.uri = uri; + this.dataSourceFactory = dataSourceFactory; + + minLoadableRetryCount = MIN_RETRY_COUNT_DEFAULT_FOR_MEDIA; + continueLoadingCheckIntervalBytes = DEFAULT_LOADING_CHECK_INTERVAL_BYTES; + } + + /** + * Sets the minimum number of times to retry if a loading error occurs. The default value is + * {@link #MIN_RETRY_COUNT_DEFAULT_FOR_MEDIA}. + * + * @param minLoadableRetryCount The minimum number of times to retry if a loading error occurs. + * @return This builder. + */ + public Builder setMinLoadableRetryCount(int minLoadableRetryCount) { + this.minLoadableRetryCount = minLoadableRetryCount; + return this; + } + + /** + * Sets the factory for {@link Extractor}s to process the media stream. Default value is an + * instance of {@link DefaultExtractorsFactory}. + * + * @param extractorsFactory A factory for {@link Extractor}s to process the media stream. If the + * possible formats are known, pass a factory that instantiates extractors for those + * formats. + * @return This builder. + */ + public Builder setExtractorsFactory(ExtractorsFactory extractorsFactory) { + this.extractorsFactory = extractorsFactory; + return this; + } + + /** + * Sets the custom key that uniquely identifies the original stream. Used for cache indexing. + * Default value is null. + * + * @param customCacheKey A custom key that uniquely identifies the original stream. Used for + * cache indexing. + * @return This builder. + */ + public Builder setCustomCacheKey(String customCacheKey) { + this.customCacheKey = customCacheKey; + return this; + } + + /** + * Sets the number of bytes that should be loaded between each invocation of + * {@link MediaPeriod.Callback#onContinueLoadingRequested(SequenceableLoader)}. Default value + * is {@link #DEFAULT_LOADING_CHECK_INTERVAL_BYTES}. + * + * @param continueLoadingCheckIntervalBytes The number of bytes that should be loaded between + * each invocation of + * {@link MediaPeriod.Callback#onContinueLoadingRequested(SequenceableLoader)}. + * @return This builder. + */ + public Builder setContinueLoadingCheckIntervalBytes(int continueLoadingCheckIntervalBytes) { + this.continueLoadingCheckIntervalBytes = continueLoadingCheckIntervalBytes; + return this; + } + + /** + * Sets the listener to respond to {@link ExtractorMediaSource} 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, EventListener eventListener) { + this.eventHandler = eventHandler; + this.eventListener = eventListener; + return this; + } + + /** + * Builds a new {@link ExtractorMediaSource} using the current parameters. + *

+ * After this call, the builder should not be re-used. + * + * @return The newly built {@link ExtractorMediaSource}. + */ + public ExtractorMediaSource build() { + Assertions.checkArgument((eventListener == null) == (eventHandler == null)); + Assertions.checkState(!isBuildCalled); + isBuildCalled = true; + if (extractorsFactory == null) { + extractorsFactory = new DefaultExtractorsFactory(); + } + return new ExtractorMediaSource(uri, dataSourceFactory, extractorsFactory, + minLoadableRetryCount, eventHandler, eventListener, customCacheKey, + continueLoadingCheckIntervalBytes); + } + + } + /** * @param uri The {@link Uri} of the media stream. * @param dataSourceFactory A factory for {@link DataSource}s to read the media. @@ -106,7 +223,9 @@ public final class ExtractorMediaSource implements MediaSource, ExtractorMediaPe * Otherwise, pass a {@link DefaultExtractorsFactory} to use default extractors. * @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 public ExtractorMediaSource(Uri uri, DataSource.Factory dataSourceFactory, ExtractorsFactory extractorsFactory, Handler eventHandler, EventListener eventListener) { this(uri, dataSourceFactory, extractorsFactory, eventHandler, eventListener, null); @@ -122,7 +241,9 @@ public final class ExtractorMediaSource implements MediaSource, ExtractorMediaPe * @param eventListener A listener of events. May be null if delivery of events is not required. * @param customCacheKey A custom key that uniquely identifies the original stream. Used for cache * indexing. May be null. + * @deprecated Use {@link Builder} instead. */ + @Deprecated public ExtractorMediaSource(Uri uri, DataSource.Factory dataSourceFactory, ExtractorsFactory extractorsFactory, Handler eventHandler, EventListener eventListener, String customCacheKey) { @@ -143,7 +264,9 @@ public final class ExtractorMediaSource implements MediaSource, ExtractorMediaPe * indexing. May be null. * @param continueLoadingCheckIntervalBytes The number of bytes that should be loaded between each * invocation of {@link MediaPeriod.Callback#onContinueLoadingRequested(SequenceableLoader)}. + * @deprecated Use {@link Builder} instead. */ + @Deprecated public ExtractorMediaSource(Uri uri, DataSource.Factory dataSourceFactory, ExtractorsFactory extractorsFactory, int minLoadableRetryCount, Handler eventHandler, EventListener eventListener, String customCacheKey, int continueLoadingCheckIntervalBytes) { 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 18aa8a63e7..397b8effd3 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 @@ -23,7 +23,6 @@ import android.view.ViewGroup; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.ExoPlayer; import com.google.android.exoplayer2.Timeline; -import com.google.android.exoplayer2.extractor.DefaultExtractorsFactory; import com.google.android.exoplayer2.source.ExtractorMediaSource; import com.google.android.exoplayer2.source.MediaPeriod; import com.google.android.exoplayer2.source.MediaSource; @@ -173,9 +172,10 @@ public final class AdsMediaSource implements MediaSource { final int adGroupIndex = id.adGroupIndex; final int adIndexInAdGroup = id.adIndexInAdGroup; if (adGroupMediaSources[adGroupIndex].length <= adIndexInAdGroup) { - MediaSource adMediaSource = new ExtractorMediaSource( - adPlaybackState.adUris[id.adGroupIndex][id.adIndexInAdGroup], dataSourceFactory, - new DefaultExtractorsFactory(), mainHandler, componentListener); + MediaSource adMediaSource = new ExtractorMediaSource.Builder( + adPlaybackState.adUris[id.adGroupIndex][id.adIndexInAdGroup], dataSourceFactory) + .setEventListener(mainHandler, componentListener) + .build(); int oldAdCount = adGroupMediaSources[id.adGroupIndex].length; if (adIndexInAdGroup >= oldAdCount) { int adCount = adIndexInAdGroup + 1;