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 bfaec2f158..c496372031 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 @@ -68,40 +68,6 @@ public final class ExtractorMediaSource extends BaseMediaSource } - /** - * The default minimum number of times to retry loading prior to failing for on-demand streams. - */ - public static final int DEFAULT_MIN_LOADABLE_RETRY_COUNT_ON_DEMAND = 3; - - /** - * The default minimum number of times to retry loading prior to failing for live streams. - */ - public static final int DEFAULT_MIN_LOADABLE_RETRY_COUNT_LIVE = 6; - - /** - * Value for {@code minLoadableRetryCount} that causes the loader to retry - * {@link #DEFAULT_MIN_LOADABLE_RETRY_COUNT_LIVE} times for live streams and - * {@link #DEFAULT_MIN_LOADABLE_RETRY_COUNT_ON_DEMAND} for on-demand streams. - */ - public static final int MIN_RETRY_COUNT_DEFAULT_FOR_MEDIA = -1; - - /** - * 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 int minLoadableRetryCount; - private final String customCacheKey; - private final int continueLoadingCheckIntervalBytes; - private final @Nullable Object tag; - - private long timelineDurationUs; - private boolean timelineIsSeekable; - /** Factory for {@link ExtractorMediaSource}s. */ public static final class Factory implements AdsMediaSource.MediaSourceFactory { @@ -244,6 +210,39 @@ public final class ExtractorMediaSource extends BaseMediaSource } } + /** + * The default minimum number of times to retry loading prior to failing for on-demand streams. + */ + public static final int DEFAULT_MIN_LOADABLE_RETRY_COUNT_ON_DEMAND = 3; + + /** The default minimum number of times to retry loading prior to failing for live streams. */ + public static final int DEFAULT_MIN_LOADABLE_RETRY_COUNT_LIVE = 6; + + /** + * Value for {@code minLoadableRetryCount} that causes the loader to retry {@link + * #DEFAULT_MIN_LOADABLE_RETRY_COUNT_LIVE} times for live streams and {@link + * #DEFAULT_MIN_LOADABLE_RETRY_COUNT_ON_DEMAND} for on-demand streams. + */ + public static final int MIN_RETRY_COUNT_DEFAULT_FOR_MEDIA = -1; + + /** + * 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 int minLoadableRetryCount; + private final String customCacheKey; + private final int continueLoadingCheckIntervalBytes; + private final @Nullable Object tag; + + private long timelineDurationUs; + private boolean timelineIsSeekable; + private @Nullable TransferListener transferListener; + /** * @param uri The {@link Uri} of the media stream. * @param dataSourceFactory A factory for {@link DataSource}s to read the media. @@ -349,6 +348,7 @@ public final class ExtractorMediaSource extends BaseMediaSource ExoPlayer player, boolean isTopLevelSource, @Nullable TransferListener mediaTransferListener) { + transferListener = mediaTransferListener; notifySourceInfoRefreshed(timelineDurationUs, /* isSeekable= */ false); } @@ -360,9 +360,13 @@ public final class ExtractorMediaSource extends BaseMediaSource @Override public MediaPeriod createPeriod(MediaPeriodId id, Allocator allocator) { Assertions.checkArgument(id.periodIndex == 0); + DataSource dataSource = dataSourceFactory.createDataSource(); + if (transferListener != null) { + dataSource.addTransferListener(transferListener); + } return new ExtractorMediaPeriod( uri, - dataSourceFactory.createDataSource(), + dataSource, extractorsFactory.createExtractors(), minLoadableRetryCount, createEventDispatcher(id), diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/SingleSampleMediaPeriod.java b/library/core/src/main/java/com/google/android/exoplayer2/source/SingleSampleMediaPeriod.java index 4de2a10f2e..3793db8128 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/source/SingleSampleMediaPeriod.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/SingleSampleMediaPeriod.java @@ -15,6 +15,7 @@ */ package com.google.android.exoplayer2.source; +import android.support.annotation.Nullable; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.FormatHolder; @@ -28,6 +29,7 @@ import com.google.android.exoplayer2.upstream.Loader; import com.google.android.exoplayer2.upstream.Loader.LoadErrorAction; import com.google.android.exoplayer2.upstream.Loader.Loadable; import com.google.android.exoplayer2.upstream.StatsDataSource; +import com.google.android.exoplayer2.upstream.TransferListener; import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.Util; import java.io.IOException; @@ -47,6 +49,7 @@ import java.util.Arrays; private final DataSpec dataSpec; private final DataSource.Factory dataSourceFactory; + private final @Nullable TransferListener transferListener; private final int minLoadableRetryCount; private final EventDispatcher eventDispatcher; private final TrackGroupArray tracks; @@ -67,6 +70,7 @@ import java.util.Arrays; public SingleSampleMediaPeriod( DataSpec dataSpec, DataSource.Factory dataSourceFactory, + @Nullable TransferListener transferListener, Format format, long durationUs, int minLoadableRetryCount, @@ -74,6 +78,7 @@ import java.util.Arrays; boolean treatLoadErrorsAsEndOfStream) { this.dataSpec = dataSpec; this.dataSourceFactory = dataSourceFactory; + this.transferListener = transferListener; this.format = format; this.durationUs = durationUs; this.minLoadableRetryCount = minLoadableRetryCount; @@ -138,11 +143,13 @@ import java.util.Arrays; if (loadingFinished || loader.isLoading()) { return false; } + DataSource dataSource = dataSourceFactory.createDataSource(); + if (transferListener != null) { + dataSource.addTransferListener(transferListener); + } long elapsedRealtimeMs = loader.startLoading( - new SourceLoadable(dataSpec, dataSourceFactory.createDataSource()), - this, - minLoadableRetryCount); + new SourceLoadable(dataSpec, dataSource), /* callback= */ this, minLoadableRetryCount); eventDispatcher.loadStarted( dataSpec, dataSpec.uri, 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 8e022e9ed7..3aaafa8706 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 @@ -169,6 +169,8 @@ public final class SingleSampleMediaSource extends BaseMediaSource { private final boolean treatLoadErrorsAsEndOfStream; private final Timeline timeline; + private @Nullable TransferListener transferListener; + /** * @param uri The {@link Uri} of the media stream. * @param dataSourceFactory The factory from which the {@link DataSource} to read the media will @@ -273,6 +275,7 @@ public final class SingleSampleMediaSource extends BaseMediaSource { ExoPlayer player, boolean isTopLevelSource, @Nullable TransferListener mediaTransferListener) { + transferListener = mediaTransferListener; refreshSourceInfo(timeline, /* manifest= */ null); } @@ -287,6 +290,7 @@ public final class SingleSampleMediaSource extends BaseMediaSource { return new SingleSampleMediaPeriod( dataSpec, dataSourceFactory, + transferListener, format, durationUs, minLoadableRetryCount, diff --git a/library/core/src/test/java/com/google/android/exoplayer2/ExoPlayerTest.java b/library/core/src/test/java/com/google/android/exoplayer2/ExoPlayerTest.java index 7ae6c870a0..c523fab2a7 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/ExoPlayerTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/ExoPlayerTest.java @@ -581,7 +581,8 @@ public final class ExoPlayerTest { MediaPeriodId id, TrackGroupArray trackGroupArray, Allocator allocator, - EventDispatcher eventDispatcher) { + EventDispatcher eventDispatcher, + @Nullable TransferListener transferListener) { FakeMediaPeriod mediaPeriod = new FakeMediaPeriod(trackGroupArray, eventDispatcher); mediaPeriod.setSeekToUsOffset(10); return mediaPeriod; @@ -615,7 +616,8 @@ public final class ExoPlayerTest { MediaPeriodId id, TrackGroupArray trackGroupArray, Allocator allocator, - EventDispatcher eventDispatcher) { + EventDispatcher eventDispatcher, + @Nullable TransferListener transferListener) { FakeMediaPeriod mediaPeriod = new FakeMediaPeriod(trackGroupArray, eventDispatcher); mediaPeriod.setDiscontinuityPositionUs(10); return mediaPeriod; @@ -640,7 +642,8 @@ public final class ExoPlayerTest { MediaPeriodId id, TrackGroupArray trackGroupArray, Allocator allocator, - EventDispatcher eventDispatcher) { + EventDispatcher eventDispatcher, + @Nullable TransferListener transferListener) { FakeMediaPeriod mediaPeriod = new FakeMediaPeriod(trackGroupArray, eventDispatcher); mediaPeriod.setDiscontinuityPositionUs(0); return mediaPeriod; @@ -876,7 +879,8 @@ public final class ExoPlayerTest { MediaPeriodId id, TrackGroupArray trackGroupArray, Allocator allocator, - EventDispatcher eventDispatcher) { + EventDispatcher eventDispatcher, + @Nullable TransferListener transferListener) { // Defer completing preparation of the period until playback parameters have been set. fakeMediaPeriodHolder[0] = new FakeMediaPeriod(trackGroupArray, eventDispatcher, /* deferOnPrepared= */ true); diff --git a/library/core/src/test/java/com/google/android/exoplayer2/source/ClippingMediaSourceTest.java b/library/core/src/test/java/com/google/android/exoplayer2/source/ClippingMediaSourceTest.java index e853529ae6..df107587a7 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/source/ClippingMediaSourceTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/source/ClippingMediaSourceTest.java @@ -37,6 +37,8 @@ import com.google.android.exoplayer2.testutil.MediaSourceTestRunner; import com.google.android.exoplayer2.testutil.RobolectricUtil; import com.google.android.exoplayer2.testutil.TimelineAsserts; import com.google.android.exoplayer2.upstream.Allocator; +import com.google.android.exoplayer2.upstream.DataSource; +import com.google.android.exoplayer2.upstream.TransferListener; import java.io.IOException; import org.junit.Before; import org.junit.Test; @@ -478,7 +480,8 @@ public final class ClippingMediaSourceTest { MediaPeriodId id, TrackGroupArray trackGroupArray, Allocator allocator, - EventDispatcher eventDispatcher) { + EventDispatcher eventDispatcher, + @Nullable TransferListener transferListener) { eventDispatcher.downstreamFormatChanged( new MediaLoadData( C.DATA_TYPE_MEDIA, @@ -488,7 +491,8 @@ public final class ClippingMediaSourceTest { /* trackSelectionData= */ null, C.usToMs(eventStartUs), C.usToMs(eventEndUs))); - return super.createFakeMediaPeriod(id, trackGroupArray, allocator, eventDispatcher); + return super.createFakeMediaPeriod( + id, trackGroupArray, allocator, eventDispatcher, transferListener); } }; final ClippingMediaSource clippingMediaSource = diff --git a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DashChunkSource.java b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DashChunkSource.java index 31c32e6100..f2a191829e 100644 --- a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DashChunkSource.java +++ b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DashChunkSource.java @@ -21,7 +21,9 @@ import com.google.android.exoplayer2.source.chunk.ChunkSource; import com.google.android.exoplayer2.source.dash.PlayerEmsgHandler.PlayerTrackEmsgHandler; import com.google.android.exoplayer2.source.dash.manifest.DashManifest; import com.google.android.exoplayer2.trackselection.TrackSelection; +import com.google.android.exoplayer2.upstream.DataSource; import com.google.android.exoplayer2.upstream.LoaderErrorThrower; +import com.google.android.exoplayer2.upstream.TransferListener; /** * An {@link ChunkSource} for DASH streams. @@ -44,6 +46,8 @@ public interface DashChunkSource extends ChunkSource { * message track. * @param enableCea608Track Whether the chunks generated by the source may output a CEA-608 * track. + * @param transferListener The transfer listener which should be informed of any data transfers. + * May be null if no listener is available. * @return The created {@link DashChunkSource}. */ DashChunkSource createDashChunkSource( @@ -56,7 +60,8 @@ public interface DashChunkSource extends ChunkSource { long elapsedRealtimeOffsetMs, boolean enableEventMessageTrack, boolean enableCea608Track, - @Nullable PlayerTrackEmsgHandler playerEmsgHandler); + @Nullable PlayerTrackEmsgHandler playerEmsgHandler, + @Nullable TransferListener transferListener); } /** diff --git a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaPeriod.java b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaPeriod.java index f80ff89fc1..1a17049921 100644 --- a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaPeriod.java +++ b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaPeriod.java @@ -43,7 +43,9 @@ import com.google.android.exoplayer2.source.dash.manifest.Period; import com.google.android.exoplayer2.source.dash.manifest.Representation; import com.google.android.exoplayer2.trackselection.TrackSelection; import com.google.android.exoplayer2.upstream.Allocator; +import com.google.android.exoplayer2.upstream.DataSource; import com.google.android.exoplayer2.upstream.LoaderErrorThrower; +import com.google.android.exoplayer2.upstream.TransferListener; import com.google.android.exoplayer2.util.MimeTypes; import java.io.IOException; import java.lang.annotation.Retention; @@ -61,6 +63,7 @@ import java.util.List; /* package */ final int id; private final DashChunkSource.Factory chunkSourceFactory; + private final @Nullable TransferListener transferListener; private final int minLoadableRetryCount; private final EventDispatcher eventDispatcher; private final long elapsedRealtimeOffset; @@ -87,6 +90,7 @@ import java.util.List; DashManifest manifest, int periodIndex, DashChunkSource.Factory chunkSourceFactory, + @Nullable TransferListener transferListener, int minLoadableRetryCount, EventDispatcher eventDispatcher, long elapsedRealtimeOffset, @@ -98,6 +102,7 @@ import java.util.List; this.manifest = manifest; this.periodIndex = periodIndex; this.chunkSourceFactory = chunkSourceFactory; + this.transferListener = transferListener; this.minLoadableRetryCount = minLoadableRetryCount; this.eventDispatcher = eventDispatcher; this.elapsedRealtimeOffset = elapsedRealtimeOffset; @@ -562,7 +567,8 @@ import java.util.List; elapsedRealtimeOffset, enableEventMessageTrack, enableCea608Track, - trackPlayerEmsgHandler); + trackPlayerEmsgHandler, + transferListener); ChunkSampleStream stream = new ChunkSampleStream<>( trackGroupInfo.trackType, 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 b90989825e..c2bde5bc6a 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 @@ -315,6 +315,7 @@ public final class DashMediaSource extends BaseMediaSource { private DataSource dataSource; private Loader loader; + private @Nullable TransferListener mediaTransferListener; private IOException manifestFatalError; private Handler handler; @@ -550,6 +551,7 @@ public final class DashMediaSource extends BaseMediaSource { ExoPlayer player, boolean isTopLevelSource, @Nullable TransferListener mediaTransferListener) { + this.mediaTransferListener = mediaTransferListener; if (sideloadedManifest) { processManifest(false); } else { @@ -576,6 +578,7 @@ public final class DashMediaSource extends BaseMediaSource { manifest, periodIndex, chunkSourceFactory, + mediaTransferListener, minLoadableRetryCount, periodEventDispatcher, elapsedRealtimeOffsetMs, diff --git a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DefaultDashChunkSource.java b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DefaultDashChunkSource.java index d226b78410..ff76dd37de 100644 --- a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DefaultDashChunkSource.java +++ b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DefaultDashChunkSource.java @@ -50,6 +50,7 @@ import com.google.android.exoplayer2.upstream.DataSource; import com.google.android.exoplayer2.upstream.DataSpec; import com.google.android.exoplayer2.upstream.HttpDataSource.InvalidResponseCodeException; import com.google.android.exoplayer2.upstream.LoaderErrorThrower; +import com.google.android.exoplayer2.upstream.TransferListener; import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.Util; import java.io.IOException; @@ -87,8 +88,12 @@ public class DefaultDashChunkSource implements DashChunkSource { long elapsedRealtimeOffsetMs, boolean enableEventMessageTrack, boolean enableCea608Track, - @Nullable PlayerTrackEmsgHandler playerEmsgHandler) { + @Nullable PlayerTrackEmsgHandler playerEmsgHandler, + @Nullable TransferListener transferListener) { DataSource dataSource = dataSourceFactory.createDataSource(); + if (transferListener != null) { + dataSource.addTransferListener(transferListener); + } return new DefaultDashChunkSource( manifestLoaderErrorThrower, manifest, diff --git a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsChunkSource.java b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsChunkSource.java index fd74657e2e..03a2af774c 100644 --- a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsChunkSource.java +++ b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsChunkSource.java @@ -17,6 +17,7 @@ package com.google.android.exoplayer2.source.hls; import android.net.Uri; import android.os.SystemClock; +import android.support.annotation.Nullable; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.source.BehindLiveWindowException; @@ -32,6 +33,7 @@ import com.google.android.exoplayer2.trackselection.BaseTrackSelection; import com.google.android.exoplayer2.trackselection.TrackSelection; import com.google.android.exoplayer2.upstream.DataSource; import com.google.android.exoplayer2.upstream.DataSpec; +import com.google.android.exoplayer2.upstream.TransferListener; import com.google.android.exoplayer2.util.TimestampAdjuster; import com.google.android.exoplayer2.util.UriUtil; import com.google.android.exoplayer2.util.Util; @@ -114,15 +116,22 @@ import java.util.List; * @param variants The available variants. * @param dataSourceFactory An {@link HlsDataSourceFactory} to create {@link DataSource}s for the * chunks. - * @param timestampAdjusterProvider A provider of {@link TimestampAdjuster} instances. If - * multiple {@link HlsChunkSource}s are used for a single playback, they should all share the - * same provider. + * @param mediaTransferListener The transfer listener which should be informed of any media data + * transfers. May be null if no listener is available. + * @param timestampAdjusterProvider A provider of {@link TimestampAdjuster} instances. If multiple + * {@link HlsChunkSource}s are used for a single playback, they should all share the same + * provider. * @param muxedCaptionFormats List of muxed caption {@link Format}s. Null if no closed caption * information is available in the master playlist. */ - public HlsChunkSource(HlsExtractorFactory extractorFactory, HlsPlaylistTracker playlistTracker, - HlsUrl[] variants, HlsDataSourceFactory dataSourceFactory, - TimestampAdjusterProvider timestampAdjusterProvider, List muxedCaptionFormats) { + public HlsChunkSource( + HlsExtractorFactory extractorFactory, + HlsPlaylistTracker playlistTracker, + HlsUrl[] variants, + HlsDataSourceFactory dataSourceFactory, + @Nullable TransferListener mediaTransferListener, + TimestampAdjusterProvider timestampAdjusterProvider, + List muxedCaptionFormats) { this.extractorFactory = extractorFactory; this.playlistTracker = playlistTracker; this.variants = variants; @@ -136,6 +145,9 @@ import java.util.List; initialTrackSelection[i] = i; } mediaDataSource = dataSourceFactory.createDataSource(C.DATA_TYPE_MEDIA); + if (mediaTransferListener != null) { + mediaDataSource.addTransferListener(mediaTransferListener); + } encryptionDataSource = dataSourceFactory.createDataSource(C.DATA_TYPE_DRM); trackGroup = new TrackGroup(variantFormats); trackSelection = new InitializationTrackSelection(trackGroup, initialTrackSelection); diff --git a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaPeriod.java b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaPeriod.java index b142d38df9..baead6630b 100644 --- a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaPeriod.java +++ b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaPeriod.java @@ -31,6 +31,8 @@ import com.google.android.exoplayer2.source.hls.playlist.HlsMasterPlaylist.HlsUr import com.google.android.exoplayer2.source.hls.playlist.HlsPlaylistTracker; import com.google.android.exoplayer2.trackselection.TrackSelection; import com.google.android.exoplayer2.upstream.Allocator; +import com.google.android.exoplayer2.upstream.DataSource; +import com.google.android.exoplayer2.upstream.TransferListener; import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.Util; @@ -50,6 +52,7 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsSampleStreamWrapper private final HlsExtractorFactory extractorFactory; private final HlsPlaylistTracker playlistTracker; private final HlsDataSourceFactory dataSourceFactory; + private final @Nullable TransferListener mediaTransferListener; private final int minLoadableRetryCount; private final EventDispatcher eventDispatcher; private final Allocator allocator; @@ -70,6 +73,7 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsSampleStreamWrapper HlsExtractorFactory extractorFactory, HlsPlaylistTracker playlistTracker, HlsDataSourceFactory dataSourceFactory, + @Nullable TransferListener mediaTransferListener, int minLoadableRetryCount, EventDispatcher eventDispatcher, Allocator allocator, @@ -78,6 +82,7 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsSampleStreamWrapper this.extractorFactory = extractorFactory; this.playlistTracker = playlistTracker; this.dataSourceFactory = dataSourceFactory; + this.mediaTransferListener = mediaTransferListener; this.minLoadableRetryCount = minLoadableRetryCount; this.eventDispatcher = eventDispatcher; this.allocator = allocator; @@ -488,8 +493,15 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsSampleStreamWrapper private HlsSampleStreamWrapper buildSampleStreamWrapper(int trackType, HlsUrl[] variants, Format muxedAudioFormat, List muxedCaptionFormats, long positionUs) { - HlsChunkSource defaultChunkSource = new HlsChunkSource(extractorFactory, playlistTracker, - variants, dataSourceFactory, timestampAdjusterProvider, muxedCaptionFormats); + HlsChunkSource defaultChunkSource = + new HlsChunkSource( + extractorFactory, + playlistTracker, + variants, + dataSourceFactory, + mediaTransferListener, + timestampAdjusterProvider, + muxedCaptionFormats); return new HlsSampleStreamWrapper(trackType, this, defaultChunkSource, allocator, positionUs, muxedAudioFormat, minLoadableRetryCount, eventDispatcher); } diff --git a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaSource.java b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaSource.java index f27342e587..3dc34462d8 100644 --- a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaSource.java +++ b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaSource.java @@ -265,6 +265,8 @@ public final class HlsMediaSource extends BaseMediaSource private final HlsPlaylistTracker playlistTracker; private final @Nullable Object tag; + private @Nullable TransferListener mediaTransferListener; + /** * @param manifestUri The {@link Uri} of the HLS manifest. * @param dataSourceFactory An {@link HlsDataSourceFactory} for {@link DataSource}s for manifests, @@ -372,6 +374,7 @@ public final class HlsMediaSource extends BaseMediaSource ExoPlayer player, boolean isTopLevelSource, @Nullable TransferListener mediaTransferListener) { + this.mediaTransferListener = mediaTransferListener; EventDispatcher eventDispatcher = createEventDispatcher(/* mediaPeriodId= */ null); playlistTracker.start(manifestUri, eventDispatcher, /* listener= */ this); } @@ -389,6 +392,7 @@ public final class HlsMediaSource extends BaseMediaSource extractorFactory, playlistTracker, dataSourceFactory, + mediaTransferListener, minLoadableRetryCount, eventDispatcher, allocator, diff --git a/library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/DefaultSsChunkSource.java b/library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/DefaultSsChunkSource.java index 20704346a1..b91df11d49 100644 --- a/library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/DefaultSsChunkSource.java +++ b/library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/DefaultSsChunkSource.java @@ -16,6 +16,7 @@ package com.google.android.exoplayer2.source.smoothstreaming; import android.net.Uri; +import android.support.annotation.Nullable; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.SeekParameters; @@ -35,6 +36,7 @@ import com.google.android.exoplayer2.trackselection.TrackSelection; import com.google.android.exoplayer2.upstream.DataSource; import com.google.android.exoplayer2.upstream.DataSpec; import com.google.android.exoplayer2.upstream.LoaderErrorThrower; +import com.google.android.exoplayer2.upstream.TransferListener; import com.google.android.exoplayer2.util.Util; import java.io.IOException; import java.util.List; @@ -53,10 +55,17 @@ public class DefaultSsChunkSource implements SsChunkSource { } @Override - public SsChunkSource createChunkSource(LoaderErrorThrower manifestLoaderErrorThrower, - SsManifest manifest, int elementIndex, TrackSelection trackSelection, - TrackEncryptionBox[] trackEncryptionBoxes) { + public SsChunkSource createChunkSource( + LoaderErrorThrower manifestLoaderErrorThrower, + SsManifest manifest, + int elementIndex, + TrackSelection trackSelection, + TrackEncryptionBox[] trackEncryptionBoxes, + @Nullable TransferListener transferListener) { DataSource dataSource = dataSourceFactory.createDataSource(); + if (transferListener != null) { + dataSource.addTransferListener(transferListener); + } return new DefaultSsChunkSource(manifestLoaderErrorThrower, manifest, elementIndex, trackSelection, dataSource, trackEncryptionBoxes); } diff --git a/library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/SsChunkSource.java b/library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/SsChunkSource.java index 48491cd0bd..0d72a22fbf 100644 --- a/library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/SsChunkSource.java +++ b/library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/SsChunkSource.java @@ -15,11 +15,14 @@ */ package com.google.android.exoplayer2.source.smoothstreaming; +import android.support.annotation.Nullable; import com.google.android.exoplayer2.extractor.mp4.TrackEncryptionBox; import com.google.android.exoplayer2.source.chunk.ChunkSource; import com.google.android.exoplayer2.source.smoothstreaming.manifest.SsManifest; import com.google.android.exoplayer2.trackselection.TrackSelection; +import com.google.android.exoplayer2.upstream.DataSource; import com.google.android.exoplayer2.upstream.LoaderErrorThrower; +import com.google.android.exoplayer2.upstream.TransferListener; /** * A {@link ChunkSource} for SmoothStreaming. @@ -37,6 +40,8 @@ public interface SsChunkSource extends ChunkSource { * @param streamElementIndex The index of the corresponding stream element in the manifest. * @param trackSelection The track selection. * @param trackEncryptionBoxes Track encryption boxes for the stream. + * @param transferListener The transfer listener which should be informed of any data transfers. + * May be null if no listener is available. * @return The created {@link SsChunkSource}. */ SsChunkSource createChunkSource( @@ -44,7 +49,8 @@ public interface SsChunkSource extends ChunkSource { SsManifest manifest, int streamElementIndex, TrackSelection trackSelection, - TrackEncryptionBox[] trackEncryptionBoxes); + TrackEncryptionBox[] trackEncryptionBoxes, + @Nullable TransferListener transferListener); } /** diff --git a/library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/SsMediaPeriod.java b/library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/SsMediaPeriod.java index 8e7c3e38c9..6e5ed93261 100644 --- a/library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/SsMediaPeriod.java +++ b/library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/SsMediaPeriod.java @@ -32,7 +32,9 @@ import com.google.android.exoplayer2.source.smoothstreaming.manifest.SsManifest; import com.google.android.exoplayer2.source.smoothstreaming.manifest.SsManifest.ProtectionElement; import com.google.android.exoplayer2.trackselection.TrackSelection; import com.google.android.exoplayer2.upstream.Allocator; +import com.google.android.exoplayer2.upstream.DataSource; import com.google.android.exoplayer2.upstream.LoaderErrorThrower; +import com.google.android.exoplayer2.upstream.TransferListener; import java.io.IOException; import java.util.ArrayList; @@ -45,6 +47,7 @@ import java.util.ArrayList; private static final int INITIALIZATION_VECTOR_SIZE = 8; private final SsChunkSource.Factory chunkSourceFactory; + private final @Nullable TransferListener transferListener; private final LoaderErrorThrower manifestLoaderErrorThrower; private final int minLoadableRetryCount; private final EventDispatcher eventDispatcher; @@ -59,11 +62,17 @@ import java.util.ArrayList; private SequenceableLoader compositeSequenceableLoader; private boolean notifiedReadingStarted; - public SsMediaPeriod(SsManifest manifest, SsChunkSource.Factory chunkSourceFactory, + public SsMediaPeriod( + SsManifest manifest, + SsChunkSource.Factory chunkSourceFactory, + @Nullable TransferListener transferListener, CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory, - int minLoadableRetryCount, EventDispatcher eventDispatcher, - LoaderErrorThrower manifestLoaderErrorThrower, Allocator allocator) { + int minLoadableRetryCount, + EventDispatcher eventDispatcher, + LoaderErrorThrower manifestLoaderErrorThrower, + Allocator allocator) { this.chunkSourceFactory = chunkSourceFactory; + this.transferListener = transferListener; this.manifestLoaderErrorThrower = manifestLoaderErrorThrower; this.minLoadableRetryCount = minLoadableRetryCount; this.eventDispatcher = eventDispatcher; @@ -214,8 +223,14 @@ import java.util.ArrayList; private ChunkSampleStream buildSampleStream(TrackSelection selection, long positionUs) { int streamElementIndex = trackGroups.indexOf(selection.getTrackGroup()); - SsChunkSource chunkSource = chunkSourceFactory.createChunkSource(manifestLoaderErrorThrower, - manifest, streamElementIndex, selection, trackEncryptionBoxes); + SsChunkSource chunkSource = + chunkSourceFactory.createChunkSource( + manifestLoaderErrorThrower, + manifest, + streamElementIndex, + selection, + trackEncryptionBoxes, + transferListener); return new ChunkSampleStream<>( manifest.streamElements[streamElementIndex].type, null, diff --git a/library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/SsMediaSource.java b/library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/SsMediaSource.java index a5544cb286..4ef3e3a80d 100644 --- a/library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/SsMediaSource.java +++ b/library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/SsMediaSource.java @@ -285,6 +285,7 @@ public final class SsMediaSource extends BaseMediaSource private DataSource manifestDataSource; private Loader manifestLoader; private LoaderErrorThrower manifestLoaderErrorThrower; + private @Nullable TransferListener mediaTransferListener; private long manifestLoadStartTimestamp; private SsManifest manifest; @@ -467,6 +468,7 @@ public final class SsMediaSource extends BaseMediaSource ExoPlayer player, boolean isTopLevelSource, @Nullable TransferListener mediaTransferListener) { + this.mediaTransferListener = mediaTransferListener; if (sideloadedManifest) { manifestLoaderErrorThrower = new LoaderErrorThrower.Dummy(); processManifest(); @@ -488,9 +490,16 @@ public final class SsMediaSource extends BaseMediaSource public MediaPeriod createPeriod(MediaPeriodId id, Allocator allocator) { Assertions.checkArgument(id.periodIndex == 0); EventDispatcher eventDispatcher = createEventDispatcher(id); - SsMediaPeriod period = new SsMediaPeriod(manifest, chunkSourceFactory, - compositeSequenceableLoaderFactory, minLoadableRetryCount, eventDispatcher, - manifestLoaderErrorThrower, allocator); + SsMediaPeriod period = + new SsMediaPeriod( + manifest, + chunkSourceFactory, + mediaTransferListener, + compositeSequenceableLoaderFactory, + minLoadableRetryCount, + eventDispatcher, + manifestLoaderErrorThrower, + allocator); mediaPeriods.add(period); return period; } diff --git a/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeAdaptiveMediaPeriod.java b/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeAdaptiveMediaPeriod.java index 1008c0d561..7869967644 100644 --- a/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeAdaptiveMediaPeriod.java +++ b/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeAdaptiveMediaPeriod.java @@ -15,6 +15,7 @@ */ package com.google.android.exoplayer2.testutil; +import android.support.annotation.Nullable; import com.google.android.exoplayer2.source.CompositeSequenceableLoader; import com.google.android.exoplayer2.source.MediaPeriod; import com.google.android.exoplayer2.source.MediaSourceEventListener.EventDispatcher; @@ -24,6 +25,8 @@ import com.google.android.exoplayer2.source.TrackGroupArray; import com.google.android.exoplayer2.source.chunk.ChunkSampleStream; import com.google.android.exoplayer2.trackselection.TrackSelection; import com.google.android.exoplayer2.upstream.Allocator; +import com.google.android.exoplayer2.upstream.DataSource; +import com.google.android.exoplayer2.upstream.TransferListener; import com.google.android.exoplayer2.util.MimeTypes; import java.util.ArrayList; import java.util.List; @@ -37,6 +40,7 @@ public class FakeAdaptiveMediaPeriod extends FakeMediaPeriod private final Allocator allocator; private final FakeChunkSource.Factory chunkSourceFactory; + private final @Nullable TransferListener transferListener; private final long durationUs; private Callback callback; @@ -48,10 +52,12 @@ public class FakeAdaptiveMediaPeriod extends FakeMediaPeriod EventDispatcher eventDispatcher, Allocator allocator, FakeChunkSource.Factory chunkSourceFactory, - long durationUs) { + long durationUs, + @Nullable TransferListener transferListener) { super(trackGroupArray, eventDispatcher); this.allocator = allocator; this.chunkSourceFactory = chunkSourceFactory; + this.transferListener = transferListener; this.durationUs = durationUs; this.sampleStreams = newSampleStreamArray(0); } @@ -128,7 +134,8 @@ public class FakeAdaptiveMediaPeriod extends FakeMediaPeriod @Override protected SampleStream createSampleStream(TrackSelection trackSelection) { - FakeChunkSource chunkSource = chunkSourceFactory.createChunkSource(trackSelection, durationUs); + FakeChunkSource chunkSource = + chunkSourceFactory.createChunkSource(trackSelection, durationUs, transferListener); return new ChunkSampleStream<>( MimeTypes.getTrackType(trackSelection.getSelectedFormat().sampleMimeType), null, diff --git a/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeAdaptiveMediaSource.java b/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeAdaptiveMediaSource.java index 41488b2a3b..278530b89b 100644 --- a/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeAdaptiveMediaSource.java +++ b/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeAdaptiveMediaSource.java @@ -16,6 +16,7 @@ package com.google.android.exoplayer2.testutil; import android.os.Handler; +import android.support.annotation.Nullable; import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.Timeline.Period; import com.google.android.exoplayer2.source.MediaSource; @@ -23,6 +24,8 @@ import com.google.android.exoplayer2.source.MediaSourceEventListener; import com.google.android.exoplayer2.source.MediaSourceEventListener.EventDispatcher; import com.google.android.exoplayer2.source.TrackGroupArray; import com.google.android.exoplayer2.upstream.Allocator; +import com.google.android.exoplayer2.upstream.DataSource; +import com.google.android.exoplayer2.upstream.TransferListener; /** * Fake {@link MediaSource} that provides a given timeline. Creating the period returns a @@ -49,10 +52,16 @@ public class FakeAdaptiveMediaSource extends FakeMediaSource { MediaPeriodId id, TrackGroupArray trackGroupArray, Allocator allocator, - EventDispatcher eventDispatcher) { + EventDispatcher eventDispatcher, + @Nullable TransferListener transferListener) { Period period = timeline.getPeriod(id.periodIndex, new Period()); return new FakeAdaptiveMediaPeriod( - trackGroupArray, eventDispatcher, allocator, chunkSourceFactory, period.durationUs); + trackGroupArray, + eventDispatcher, + allocator, + chunkSourceFactory, + period.durationUs, + transferListener); } } diff --git a/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeChunkSource.java b/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeChunkSource.java index d0ee501fbd..b2922dcd18 100644 --- a/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeChunkSource.java +++ b/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeChunkSource.java @@ -16,6 +16,7 @@ package com.google.android.exoplayer2.testutil; import android.net.Uri; +import android.support.annotation.Nullable; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.SeekParameters; @@ -28,6 +29,7 @@ import com.google.android.exoplayer2.testutil.FakeDataSet.FakeData.Segment; import com.google.android.exoplayer2.trackselection.TrackSelection; import com.google.android.exoplayer2.upstream.DataSource; import com.google.android.exoplayer2.upstream.DataSpec; +import com.google.android.exoplayer2.upstream.TransferListener; import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.Util; import java.io.IOException; @@ -52,11 +54,17 @@ public final class FakeChunkSource implements ChunkSource { this.dataSourceFactory = dataSourceFactory; } - public FakeChunkSource createChunkSource(TrackSelection trackSelection, long durationUs) { + public FakeChunkSource createChunkSource( + TrackSelection trackSelection, + long durationUs, + @Nullable TransferListener transferListener) { FakeAdaptiveDataSet dataSet = dataSetFactory.createDataSet(trackSelection.getTrackGroup(), durationUs); dataSourceFactory.setFakeDataSet(dataSet); DataSource dataSource = dataSourceFactory.createDataSource(); + if (transferListener != null) { + dataSource.addTransferListener(transferListener); + } return new FakeChunkSource(trackSelection, dataSource, dataSet); } diff --git a/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeMediaSource.java b/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeMediaSource.java index 2e0bfcc9ab..62563bc749 100644 --- a/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeMediaSource.java +++ b/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeMediaSource.java @@ -61,6 +61,7 @@ public class FakeMediaSource extends BaseMediaSource { private boolean preparedSource; private boolean releasedSource; private Handler sourceInfoRefreshHandler; + private @Nullable TransferListener transferListener; /** * Creates a {@link FakeMediaSource}. This media source creates {@link FakeMediaPeriod}s with a @@ -93,6 +94,7 @@ public class FakeMediaSource extends BaseMediaSource { boolean isTopLevelSource, @Nullable TransferListener mediaTransferListener) { assertThat(preparedSource).isFalse(); + transferListener = mediaTransferListener; preparedSource = true; releasedSource = false; sourceInfoRefreshHandler = new Handler(); @@ -115,7 +117,7 @@ public class FakeMediaSource extends BaseMediaSource { EventDispatcher eventDispatcher = createEventDispatcher(period.windowIndex, id, period.getPositionInWindowMs()); FakeMediaPeriod mediaPeriod = - createFakeMediaPeriod(id, trackGroupArray, allocator, eventDispatcher); + createFakeMediaPeriod(id, trackGroupArray, allocator, eventDispatcher, transferListener); activeMediaPeriods.add(mediaPeriod); createdMediaPeriods.add(id); return mediaPeriod; @@ -195,13 +197,16 @@ public class FakeMediaSource extends BaseMediaSource { * @param trackGroupArray The {@link TrackGroupArray} supported by the media period. * @param allocator An {@link Allocator} from which to obtain media buffer allocations. * @param eventDispatcher An {@link EventDispatcher} to dispatch media source events. + * @param transferListener The transfer listener which should be informed of any data transfers. + * May be null if no listener is available. * @return A new {@link FakeMediaPeriod}. */ protected FakeMediaPeriod createFakeMediaPeriod( MediaPeriodId id, TrackGroupArray trackGroupArray, Allocator allocator, - EventDispatcher eventDispatcher) { + EventDispatcher eventDispatcher, + @Nullable TransferListener transferListener) { return new FakeMediaPeriod(trackGroupArray, eventDispatcher); }