diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 6c72dae654..65d4e9696e 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -2,6 +2,7 @@ ### dev-v2 (not yet released) ### +* `ExtractorMediaSource` renamed to `ProgressiveMediaSource`. * HLS: * Parse CHANNELS attribute from EXT-X-MEDIA. * Support for playing spherical videos on Daydream. diff --git a/demos/cast/src/main/java/com/google/android/exoplayer2/castdemo/DefaultReceiverPlayerManager.java b/demos/cast/src/main/java/com/google/android/exoplayer2/castdemo/DefaultReceiverPlayerManager.java index 563efea11f..1db68ca08d 100644 --- a/demos/cast/src/main/java/com/google/android/exoplayer2/castdemo/DefaultReceiverPlayerManager.java +++ b/demos/cast/src/main/java/com/google/android/exoplayer2/castdemo/DefaultReceiverPlayerManager.java @@ -35,8 +35,8 @@ import com.google.android.exoplayer2.ext.cast.CastPlayer; import com.google.android.exoplayer2.ext.cast.MediaItem; import com.google.android.exoplayer2.ext.cast.SessionAvailabilityListener; import com.google.android.exoplayer2.source.ConcatenatingMediaSource; -import com.google.android.exoplayer2.source.ExtractorMediaSource; import com.google.android.exoplayer2.source.MediaSource; +import com.google.android.exoplayer2.source.ProgressiveMediaSource; import com.google.android.exoplayer2.source.dash.DashMediaSource; import com.google.android.exoplayer2.source.hls.HlsMediaSource; import com.google.android.exoplayer2.source.smoothstreaming.SsMediaSource; @@ -373,7 +373,7 @@ import java.util.ArrayList; case DemoUtil.MIME_TYPE_HLS: return new HlsMediaSource.Factory(DATA_SOURCE_FACTORY).createMediaSource(uri); case DemoUtil.MIME_TYPE_VIDEO_MP4: - return new ExtractorMediaSource.Factory(DATA_SOURCE_FACTORY).createMediaSource(uri); + return new ProgressiveMediaSource.Factory(DATA_SOURCE_FACTORY).createMediaSource(uri); default: { throw new IllegalStateException("Unsupported type: " + item.mimeType); } 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 d67c4549d8..740c1a0af6 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 @@ -23,8 +23,8 @@ 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.source.ExtractorMediaSource; import com.google.android.exoplayer2.source.MediaSource; +import com.google.android.exoplayer2.source.ProgressiveMediaSource; import com.google.android.exoplayer2.source.ads.AdsMediaSource; import com.google.android.exoplayer2.source.dash.DashMediaSource; import com.google.android.exoplayer2.source.hls.HlsMediaSource; @@ -125,7 +125,7 @@ import com.google.android.exoplayer2.util.Util; case C.TYPE_HLS: return new HlsMediaSource.Factory(dataSourceFactory).createMediaSource(uri); case C.TYPE_OTHER: - return new ExtractorMediaSource.Factory(dataSourceFactory).createMediaSource(uri); + return new ProgressiveMediaSource.Factory(dataSourceFactory).createMediaSource(uri); default: throw new IllegalStateException("Unsupported type: " + type); } 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 582638b460..2d4efd7f3d 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 @@ -51,8 +51,8 @@ import com.google.android.exoplayer2.mediacodec.MediaCodecUtil.DecoderQueryExcep import com.google.android.exoplayer2.offline.StreamKey; import com.google.android.exoplayer2.source.BehindLiveWindowException; import com.google.android.exoplayer2.source.ConcatenatingMediaSource; -import com.google.android.exoplayer2.source.ExtractorMediaSource; import com.google.android.exoplayer2.source.MediaSource; +import com.google.android.exoplayer2.source.ProgressiveMediaSource; import com.google.android.exoplayer2.source.TrackGroupArray; import com.google.android.exoplayer2.source.ads.AdsLoader; import com.google.android.exoplayer2.source.ads.AdsMediaSource; @@ -483,7 +483,7 @@ public class PlayerActivity extends Activity .setStreamKeys(offlineStreamKeys) .createMediaSource(uri); case C.TYPE_OTHER: - return new ExtractorMediaSource.Factory(dataSourceFactory).createMediaSource(uri); + return new ProgressiveMediaSource.Factory(dataSourceFactory).createMediaSource(uri); 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 2efdde4e58..5448919626 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 @@ -28,8 +28,8 @@ import com.google.android.exoplayer2.ExoPlayerFactory; import com.google.android.exoplayer2.Player; import com.google.android.exoplayer2.Renderer; import com.google.android.exoplayer2.extractor.mkv.MatroskaExtractor; -import com.google.android.exoplayer2.source.ExtractorMediaSource; import com.google.android.exoplayer2.source.MediaSource; +import com.google.android.exoplayer2.source.ProgressiveMediaSource; import com.google.android.exoplayer2.trackselection.DefaultTrackSelector; import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory; import org.junit.Before; @@ -86,7 +86,7 @@ public class FlacPlaybackTest { player = ExoPlayerFactory.newInstance(context, new Renderer[] {audioRenderer}, trackSelector); player.addListener(this); MediaSource mediaSource = - new ExtractorMediaSource.Factory( + new ProgressiveMediaSource.Factory( new DefaultDataSourceFactory(context, "ExoPlayerExtFlacTest")) .setExtractorsFactory(MatroskaExtractor.FACTORY) .createMediaSource(uri); 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 5ad864c597..6a254c8230 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 @@ -28,8 +28,8 @@ import com.google.android.exoplayer2.ExoPlayerFactory; import com.google.android.exoplayer2.Player; import com.google.android.exoplayer2.Renderer; import com.google.android.exoplayer2.extractor.mkv.MatroskaExtractor; -import com.google.android.exoplayer2.source.ExtractorMediaSource; import com.google.android.exoplayer2.source.MediaSource; +import com.google.android.exoplayer2.source.ProgressiveMediaSource; import com.google.android.exoplayer2.trackselection.DefaultTrackSelector; import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory; import org.junit.Before; @@ -86,7 +86,7 @@ public class OpusPlaybackTest { player = ExoPlayerFactory.newInstance(context, new Renderer[] {audioRenderer}, trackSelector); player.addListener(this); MediaSource mediaSource = - new ExtractorMediaSource.Factory( + new ProgressiveMediaSource.Factory( new DefaultDataSourceFactory(context, "ExoPlayerExtOpusTest")) .setExtractorsFactory(MatroskaExtractor.FACTORY) .createMediaSource(uri); 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 1de461e374..a36b578588 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 @@ -29,8 +29,8 @@ import com.google.android.exoplayer2.ExoPlayerFactory; import com.google.android.exoplayer2.Player; import com.google.android.exoplayer2.Renderer; import com.google.android.exoplayer2.extractor.mkv.MatroskaExtractor; -import com.google.android.exoplayer2.source.ExtractorMediaSource; import com.google.android.exoplayer2.source.MediaSource; +import com.google.android.exoplayer2.source.ProgressiveMediaSource; import com.google.android.exoplayer2.trackselection.DefaultTrackSelector; import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory; import com.google.android.exoplayer2.util.Log; @@ -119,7 +119,7 @@ public class VpxPlaybackTest { player = ExoPlayerFactory.newInstance(context, new Renderer[] {videoRenderer}, trackSelector); player.addListener(this); MediaSource mediaSource = - new ExtractorMediaSource.Factory( + new ProgressiveMediaSource.Factory( new DefaultDataSourceFactory(context, "ExoPlayerExtVp9Test")) .setExtractorsFactory(MatroskaExtractor.FACTORY) .createMediaSource(uri); diff --git a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayer.java b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayer.java index 5ba2394c3f..db168d9c29 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayer.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayer.java @@ -21,10 +21,10 @@ import com.google.android.exoplayer2.audio.MediaCodecAudioRenderer; import com.google.android.exoplayer2.metadata.MetadataRenderer; import com.google.android.exoplayer2.source.ClippingMediaSource; import com.google.android.exoplayer2.source.ConcatenatingMediaSource; -import com.google.android.exoplayer2.source.ExtractorMediaSource; import com.google.android.exoplayer2.source.LoopingMediaSource; import com.google.android.exoplayer2.source.MediaSource; import com.google.android.exoplayer2.source.MergingMediaSource; +import com.google.android.exoplayer2.source.ProgressiveMediaSource; import com.google.android.exoplayer2.source.SingleSampleMediaSource; import com.google.android.exoplayer2.text.TextRenderer; import com.google.android.exoplayer2.trackselection.DefaultTrackSelector; @@ -48,7 +48,7 @@ import com.google.android.exoplayer2.video.MediaCodecVideoRenderer; *
If the possible input stream container formats are known, pass a factory that instantiates - * extractors for them to the constructor. Otherwise, pass a {@link DefaultExtractorsFactory} to use - * the default extractors. When reading a new stream, the first {@link Extractor} in the array of - * extractors created by the factory that returns {@code true} from {@link Extractor#sniff} will be - * used to extract samples from the input stream. - * - *
Note that the built-in extractors for AAC, MPEG PS/TS and FLV streams do not support seeking.
- */
+/** @deprecated Use {@link ProgressiveMediaSource} instead. */
+@Deprecated
+@SuppressWarnings("deprecation")
public final class ExtractorMediaSource extends BaseMediaSource
- implements ExtractorMediaPeriod.Listener {
+ implements MediaSource.SourceInfoRefreshListener {
- /**
- * Listener of {@link ExtractorMediaSource} events.
- *
- * @deprecated Use {@link MediaSourceEventListener}.
- */
+ /** @deprecated Use {@link MediaSourceEventListener} instead. */
@Deprecated
public interface EventListener {
@@ -70,7 +59,8 @@ public final class ExtractorMediaSource extends BaseMediaSource
}
- /** Factory for {@link ExtractorMediaSource}s. */
+ /** Use {@link ProgressiveMediaSource.Factory} instead. */
+ @Deprecated
public static final class Factory implements AdsMediaSource.MediaSourceFactory {
private final DataSource.Factory dataSourceFactory;
@@ -232,23 +222,11 @@ public final class ExtractorMediaSource extends BaseMediaSource
}
}
- /**
- * The default number of bytes that should be loaded between each each invocation of {@link
- * MediaPeriod.Callback#onContinueLoadingRequested(SequenceableLoader)}.
- */
- public static final int DEFAULT_LOADING_CHECK_INTERVAL_BYTES = 1024 * 1024;
+ @Deprecated
+ public static final int DEFAULT_LOADING_CHECK_INTERVAL_BYTES =
+ ProgressiveMediaSource.DEFAULT_LOADING_CHECK_INTERVAL_BYTES;
- private final Uri uri;
- private final DataSource.Factory dataSourceFactory;
- private final ExtractorsFactory extractorsFactory;
- private final LoadErrorHandlingPolicy loadableLoadErrorHandlingPolicy;
- private final String customCacheKey;
- private final int continueLoadingCheckIntervalBytes;
- private final @Nullable Object tag;
-
- private long timelineDurationUs;
- private boolean timelineIsSeekable;
- private @Nullable TransferListener transferListener;
+ private final ProgressiveMediaSource progressiveMediaSource;
/**
* @param uri The {@link Uri} of the media stream.
@@ -261,7 +239,6 @@ public final class ExtractorMediaSource extends BaseMediaSource
* @deprecated Use {@link Factory} instead.
*/
@Deprecated
- @SuppressWarnings("deprecation")
public ExtractorMediaSource(
Uri uri,
DataSource.Factory dataSourceFactory,
@@ -284,7 +261,6 @@ public final class ExtractorMediaSource extends BaseMediaSource
* @deprecated Use {@link Factory} instead.
*/
@Deprecated
- @SuppressWarnings("deprecation")
public ExtractorMediaSource(
Uri uri,
DataSource.Factory dataSourceFactory,
@@ -317,7 +293,6 @@ public final class ExtractorMediaSource extends BaseMediaSource
* @deprecated Use {@link Factory} instead.
*/
@Deprecated
- @SuppressWarnings("deprecation")
public ExtractorMediaSource(
Uri uri,
DataSource.Factory dataSourceFactory,
@@ -347,93 +322,57 @@ public final class ExtractorMediaSource extends BaseMediaSource
@Nullable String customCacheKey,
int continueLoadingCheckIntervalBytes,
@Nullable Object tag) {
- this.uri = uri;
- this.dataSourceFactory = dataSourceFactory;
- this.extractorsFactory = extractorsFactory;
- this.loadableLoadErrorHandlingPolicy = loadableLoadErrorHandlingPolicy;
- this.customCacheKey = customCacheKey;
- this.continueLoadingCheckIntervalBytes = continueLoadingCheckIntervalBytes;
- this.timelineDurationUs = C.TIME_UNSET;
- this.tag = tag;
+ progressiveMediaSource =
+ new ProgressiveMediaSource(
+ uri,
+ dataSourceFactory,
+ extractorsFactory,
+ loadableLoadErrorHandlingPolicy,
+ customCacheKey,
+ continueLoadingCheckIntervalBytes,
+ tag);
}
@Override
@Nullable
public Object getTag() {
- return tag;
+ return progressiveMediaSource.getTag();
}
@Override
public void prepareSourceInternal(@Nullable TransferListener mediaTransferListener) {
- transferListener = mediaTransferListener;
- notifySourceInfoRefreshed(timelineDurationUs, timelineIsSeekable);
+ progressiveMediaSource.prepareSource(/* listener= */ this, mediaTransferListener);
}
@Override
public void maybeThrowSourceInfoRefreshError() throws IOException {
- // Do nothing.
+ progressiveMediaSource.maybeThrowSourceInfoRefreshError();
}
@Override
public MediaPeriod createPeriod(MediaPeriodId id, Allocator allocator, long startPositionUs) {
- DataSource dataSource = dataSourceFactory.createDataSource();
- if (transferListener != null) {
- dataSource.addTransferListener(transferListener);
- }
- return new ExtractorMediaPeriod(
- uri,
- dataSource,
- extractorsFactory.createExtractors(),
- loadableLoadErrorHandlingPolicy,
- createEventDispatcher(id),
- this,
- allocator,
- customCacheKey,
- continueLoadingCheckIntervalBytes);
+ return progressiveMediaSource.createPeriod(id, allocator, startPositionUs);
}
@Override
public void releasePeriod(MediaPeriod mediaPeriod) {
- ((ExtractorMediaPeriod) mediaPeriod).release();
+ progressiveMediaSource.releasePeriod(mediaPeriod);
}
@Override
public void releaseSourceInternal() {
- // Do nothing.
+ progressiveMediaSource.releaseSource(/* listener= */ this);
}
- // ExtractorMediaPeriod.Listener implementation.
-
@Override
- public void onSourceInfoRefreshed(long durationUs, boolean isSeekable) {
- // If we already have the duration from a previous source info refresh, use it.
- durationUs = durationUs == C.TIME_UNSET ? timelineDurationUs : durationUs;
- if (timelineDurationUs == durationUs && timelineIsSeekable == isSeekable) {
- // Suppress no-op source info changes.
- return;
- }
- notifySourceInfoRefreshed(durationUs, isSeekable);
+ public void onSourceInfoRefreshed(
+ MediaSource source, Timeline timeline, @Nullable Object manifest) {
+ refreshSourceInfo(timeline, manifest);
}
- // Internal methods.
-
- private void notifySourceInfoRefreshed(long durationUs, boolean isSeekable) {
- timelineDurationUs = durationUs;
- timelineIsSeekable = isSeekable;
- // TODO: Make timeline dynamic until its duration is known. This is non-trivial. See b/69703223.
- refreshSourceInfo(
- new SinglePeriodTimeline(
- timelineDurationUs, timelineIsSeekable, /* isDynamic= */ false, tag),
- /* manifest= */ null);
- }
-
- /**
- * Wraps a deprecated {@link EventListener}, invoking its callback from the equivalent callback in
- * {@link MediaSourceEventListener}.
- */
@Deprecated
- @SuppressWarnings("deprecation")
private static final class EventListenerWrapper extends DefaultMediaSourceEventListener {
+
private final EventListener eventListener;
public EventListenerWrapper(EventListener eventListener) {
diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/ExtractorMediaPeriod.java b/library/core/src/main/java/com/google/android/exoplayer2/source/ProgressiveMediaPeriod.java
similarity index 98%
rename from library/core/src/main/java/com/google/android/exoplayer2/source/ExtractorMediaPeriod.java
rename to library/core/src/main/java/com/google/android/exoplayer2/source/ProgressiveMediaPeriod.java
index e842d4f253..41f6e986c6 100644
--- a/library/core/src/main/java/com/google/android/exoplayer2/source/ExtractorMediaPeriod.java
+++ b/library/core/src/main/java/com/google/android/exoplayer2/source/ProgressiveMediaPeriod.java
@@ -54,12 +54,13 @@ import java.io.IOException;
import java.util.Arrays;
import org.checkerframework.checker.nullness.compatqual.NullableType;
-/**
- * A {@link MediaPeriod} that extracts data using an {@link Extractor}.
- */
-/* package */ final class ExtractorMediaPeriod implements MediaPeriod, ExtractorOutput,
- Loader.Callback If the possible input stream container formats are known, pass a factory that instantiates
+ * extractors for them to the constructor. Otherwise, pass a {@link DefaultExtractorsFactory} to use
+ * the default extractors. When reading a new stream, the first {@link Extractor} in the array of
+ * extractors created by the factory that returns {@code true} from {@link Extractor#sniff} will be
+ * used to extract samples from the input stream.
+ *
+ * Note that the built-in extractors for AAC, MPEG PS/TS and FLV streams do not support seeking.
+ */
+public final class ProgressiveMediaSource extends BaseMediaSource
+ implements ProgressiveMediaPeriod.Listener {
+
+ /** Factory for {@link ProgressiveMediaSource}s. */
+ public static final class Factory implements AdsMediaSource.MediaSourceFactory {
+
+ private final DataSource.Factory dataSourceFactory;
+
+ @Nullable private ExtractorsFactory extractorsFactory;
+ @Nullable private String customCacheKey;
+ @Nullable private Object tag;
+ private LoadErrorHandlingPolicy loadErrorHandlingPolicy;
+ private int continueLoadingCheckIntervalBytes;
+ private boolean isCreateCalled;
+
+ /**
+ * Creates a new factory for {@link ProgressiveMediaSource}s.
+ *
+ * @param dataSourceFactory A factory for {@link DataSource}s to read the media.
+ */
+ public Factory(DataSource.Factory dataSourceFactory) {
+ this.dataSourceFactory = dataSourceFactory;
+ loadErrorHandlingPolicy = new DefaultLoadErrorHandlingPolicy();
+ continueLoadingCheckIntervalBytes = DEFAULT_LOADING_CHECK_INTERVAL_BYTES;
+ }
+
+ /**
+ * Sets the factory for {@link Extractor}s to process the media stream. The 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 factory, for convenience.
+ * @throws IllegalStateException If one of the {@code create} methods has already been called.
+ */
+ public Factory setExtractorsFactory(ExtractorsFactory extractorsFactory) {
+ Assertions.checkState(!isCreateCalled);
+ this.extractorsFactory = extractorsFactory;
+ return this;
+ }
+
+ /**
+ * Sets the custom key that uniquely identifies the original stream. Used for cache indexing.
+ * The default value is {@code null}.
+ *
+ * @param customCacheKey A custom key that uniquely identifies the original stream. Used for
+ * cache indexing.
+ * @return This factory, for convenience.
+ * @throws IllegalStateException If one of the {@code create} methods has already been called.
+ */
+ public Factory setCustomCacheKey(String customCacheKey) {
+ Assertions.checkState(!isCreateCalled);
+ this.customCacheKey = customCacheKey;
+ return this;
+ }
+
+ /**
+ * Sets a tag for the media source which will be published in the {@link
+ * com.google.android.exoplayer2.Timeline} of the source as {@link
+ * com.google.android.exoplayer2.Timeline.Window#tag}.
+ *
+ * @param tag A tag for the media source.
+ * @return This factory, for convenience.
+ * @throws IllegalStateException If one of the {@code create} methods has already been called.
+ */
+ public Factory setTag(Object tag) {
+ Assertions.checkState(!isCreateCalled);
+ this.tag = tag;
+ return this;
+ }
+
+ /**
+ * Sets the {@link LoadErrorHandlingPolicy}. The default value is created by calling {@link
+ * DefaultLoadErrorHandlingPolicy#DefaultLoadErrorHandlingPolicy()}.
+ *
+ * @param loadErrorHandlingPolicy A {@link LoadErrorHandlingPolicy}.
+ * @return This factory, for convenience.
+ * @throws IllegalStateException If one of the {@code create} methods has already been called.
+ */
+ public Factory setLoadErrorHandlingPolicy(LoadErrorHandlingPolicy loadErrorHandlingPolicy) {
+ Assertions.checkState(!isCreateCalled);
+ this.loadErrorHandlingPolicy = loadErrorHandlingPolicy;
+ return this;
+ }
+
+ /**
+ * Sets the number of bytes that should be loaded between each invocation of {@link
+ * MediaPeriod.Callback#onContinueLoadingRequested(SequenceableLoader)}. The 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 factory, for convenience.
+ * @throws IllegalStateException If one of the {@code create} methods has already been called.
+ */
+ public Factory setContinueLoadingCheckIntervalBytes(int continueLoadingCheckIntervalBytes) {
+ Assertions.checkState(!isCreateCalled);
+ this.continueLoadingCheckIntervalBytes = continueLoadingCheckIntervalBytes;
+ return this;
+ }
+
+ /**
+ * Returns a new {@link ProgressiveMediaSource} using the current parameters.
+ *
+ * @param uri The {@link Uri}.
+ * @return The new {@link ProgressiveMediaSource}.
+ */
+ @Override
+ public ProgressiveMediaSource createMediaSource(Uri uri) {
+ isCreateCalled = true;
+ if (extractorsFactory == null) {
+ extractorsFactory = new DefaultExtractorsFactory();
+ }
+ return new ProgressiveMediaSource(
+ uri,
+ dataSourceFactory,
+ extractorsFactory,
+ loadErrorHandlingPolicy,
+ customCacheKey,
+ continueLoadingCheckIntervalBytes,
+ tag);
+ }
+
+ @Override
+ public int[] getSupportedTypes() {
+ return new int[] {C.TYPE_OTHER};
+ }
+ }
+
+ /**
+ * The default number of bytes that should be loaded between each each invocation of {@link
+ * MediaPeriod.Callback#onContinueLoadingRequested(SequenceableLoader)}.
+ */
+ public static final int DEFAULT_LOADING_CHECK_INTERVAL_BYTES = 1024 * 1024;
+
+ private final Uri uri;
+ private final DataSource.Factory dataSourceFactory;
+ private final ExtractorsFactory extractorsFactory;
+ private final LoadErrorHandlingPolicy loadableLoadErrorHandlingPolicy;
+ @Nullable private final String customCacheKey;
+ private final int continueLoadingCheckIntervalBytes;
+ @Nullable private final Object tag;
+
+ private long timelineDurationUs;
+ private boolean timelineIsSeekable;
+ @Nullable private TransferListener transferListener;
+
+ // TODO: Make private when ExtractorMediaSource is deleted.
+ /* package */ ProgressiveMediaSource(
+ Uri uri,
+ DataSource.Factory dataSourceFactory,
+ ExtractorsFactory extractorsFactory,
+ LoadErrorHandlingPolicy loadableLoadErrorHandlingPolicy,
+ @Nullable String customCacheKey,
+ int continueLoadingCheckIntervalBytes,
+ @Nullable Object tag) {
+ this.uri = uri;
+ this.dataSourceFactory = dataSourceFactory;
+ this.extractorsFactory = extractorsFactory;
+ this.loadableLoadErrorHandlingPolicy = loadableLoadErrorHandlingPolicy;
+ this.customCacheKey = customCacheKey;
+ this.continueLoadingCheckIntervalBytes = continueLoadingCheckIntervalBytes;
+ this.timelineDurationUs = C.TIME_UNSET;
+ this.tag = tag;
+ }
+
+ @Override
+ @Nullable
+ public Object getTag() {
+ return tag;
+ }
+
+ @Override
+ public void prepareSourceInternal(@Nullable TransferListener mediaTransferListener) {
+ transferListener = mediaTransferListener;
+ notifySourceInfoRefreshed(timelineDurationUs, timelineIsSeekable);
+ }
+
+ @Override
+ public void maybeThrowSourceInfoRefreshError() throws IOException {
+ // Do nothing.
+ }
+
+ @Override
+ public MediaPeriod createPeriod(MediaPeriodId id, Allocator allocator, long startPositionUs) {
+ DataSource dataSource = dataSourceFactory.createDataSource();
+ if (transferListener != null) {
+ dataSource.addTransferListener(transferListener);
+ }
+ return new ProgressiveMediaPeriod(
+ uri,
+ dataSource,
+ extractorsFactory.createExtractors(),
+ loadableLoadErrorHandlingPolicy,
+ createEventDispatcher(id),
+ this,
+ allocator,
+ customCacheKey,
+ continueLoadingCheckIntervalBytes);
+ }
+
+ @Override
+ public void releasePeriod(MediaPeriod mediaPeriod) {
+ ((ProgressiveMediaPeriod) mediaPeriod).release();
+ }
+
+ @Override
+ public void releaseSourceInternal() {
+ // Do nothing.
+ }
+
+ // ProgressiveMediaPeriod.Listener implementation.
+
+ @Override
+ public void onSourceInfoRefreshed(long durationUs, boolean isSeekable) {
+ // If we already have the duration from a previous source info refresh, use it.
+ durationUs = durationUs == C.TIME_UNSET ? timelineDurationUs : durationUs;
+ if (timelineDurationUs == durationUs && timelineIsSeekable == isSeekable) {
+ // Suppress no-op source info changes.
+ return;
+ }
+ notifySourceInfoRefreshed(durationUs, isSeekable);
+ }
+
+ // Internal methods.
+
+ private void notifySourceInfoRefreshed(long durationUs, boolean isSeekable) {
+ timelineDurationUs = durationUs;
+ timelineIsSeekable = isSeekable;
+ // TODO: Make timeline dynamic until its duration is known. This is non-trivial. See b/69703223.
+ refreshSourceInfo(
+ new SinglePeriodTimeline(
+ timelineDurationUs, timelineIsSeekable, /* isDynamic= */ false, tag),
+ /* manifest= */ null);
+ }
+}
diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/SingleSampleMediaSource.java b/library/core/src/main/java/com/google/android/exoplayer2/source/SingleSampleMediaSource.java
index d13fa06434..7611d76260 100644
--- a/library/core/src/main/java/com/google/android/exoplayer2/source/SingleSampleMediaSource.java
+++ b/library/core/src/main/java/com/google/android/exoplayer2/source/SingleSampleMediaSource.java
@@ -138,12 +138,12 @@ public final class SingleSampleMediaSource extends BaseMediaSource {
}
/**
- * Returns a new {@link ExtractorMediaSource} using the current parameters.
+ * Returns a new {@link SingleSampleMediaSource} using the current parameters.
*
* @param uri The {@link Uri}.
* @param format The {@link Format} of the media stream.
* @param durationUs The duration of the media stream in microseconds.
- * @return The new {@link ExtractorMediaSource}.
+ * @return The new {@link SingleSampleMediaSource}.
*/
public SingleSampleMediaSource createMediaSource(Uri uri, Format format, long durationUs) {
isCreateCalled = true;
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 4754466235..e80c797eb9 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
@@ -25,13 +25,13 @@ import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.source.CompositeMediaSource;
import com.google.android.exoplayer2.source.DeferredMediaPeriod;
-import com.google.android.exoplayer2.source.ExtractorMediaSource;
import com.google.android.exoplayer2.source.MediaPeriod;
import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId;
import com.google.android.exoplayer2.source.MediaSourceEventListener;
import com.google.android.exoplayer2.source.MediaSourceEventListener.LoadEventInfo;
import com.google.android.exoplayer2.source.MediaSourceEventListener.MediaLoadData;
+import com.google.android.exoplayer2.source.ProgressiveMediaSource;
import com.google.android.exoplayer2.upstream.Allocator;
import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.DataSpec;
@@ -203,7 +203,7 @@ public final class AdsMediaSource extends CompositeMediaSource