Forward TransferListener to media sources.

In the future, this allows to register the BandwidthMeter (managed by the player)
as a listener to all media transfers related to this media source.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=202643946
This commit is contained in:
tonihei 2018-06-29 07:29:02 -07:00 committed by Oliver Woodman
parent 824c0b20a5
commit 98afaa60d0
19 changed files with 129 additions and 37 deletions

View File

@ -18,8 +18,13 @@
* Add support for lazy preparation of playlist media sources in
`ConcatenatingMediaSource`
([#3972](https://github.com/google/ExoPlayer/issues/3972)).
* Pass `BandwidthMeter` to `TrackSelection.Factory` which can be used to obtain
bandwidth estimates in the future. Always null at the moment.
* `BandwidthMeter` management (work in progress):
* Pass `BandwidthMeter` to `TrackSelection.Factory` which can be used to
obtain bandwidth estimates in the future. Always null at the moment.
* Add method to `BandwidthMeter` to return the `TransferListener` used to
gather bandwidth information.
* Pass `TransferListener` to `MediaSource`s to listen to media data transfers.
Always null at the moment.
* Error handling:
* Allow configuration of the Loader retry delay
([#3370](https://github.com/google/ExoPlayer/issues/3370)).
@ -29,8 +34,6 @@
* DRM:
* Allow DrmInitData to carry a license server URL
([#3393](https://github.com/google/ExoPlayer/issues/3393)).
* Add method to `BandwidthMeter` to return the `TransferListener` used to gather
bandwidth information.
* Add callback to `VideoListener` to notify of surface size changes.
* Fix bug when reporting buffered position for multi-period windows and add
two additional convenience methods `Player.getTotalBufferedDuration` and

View File

@ -27,6 +27,7 @@ import com.google.android.exoplayer2.source.MediaSource.SourceInfoRefreshListene
import com.google.android.exoplayer2.source.ads.AdsMediaSource;
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;
/**
@ -76,8 +77,12 @@ public final class ImaAdsMediaSource extends BaseMediaSource implements SourceIn
}
@Override
public void prepareSourceInternal(final ExoPlayer player, boolean isTopLevelSource) {
adsMediaSource.prepareSource(player, isTopLevelSource, /* listener= */ this);
public void prepareSourceInternal(
final ExoPlayer player,
boolean isTopLevelSource,
@Nullable TransferListener<?> mediaTransferListener) {
adsMediaSource.prepareSource(
player, isTopLevelSource, /* listener= */ this, mediaTransferListener);
}
@Override

View File

@ -393,7 +393,11 @@ import java.util.Collections;
loadControl.onPrepared();
this.mediaSource = mediaSource;
setState(Player.STATE_BUFFERING);
mediaSource.prepareSource(player, /* isTopLevelSource= */ true, /* listener= */ this);
mediaSource.prepareSource(
player,
/* isTopLevelSource= */ true,
/* listener= */ this,
/* mediaTransferListener= */ null);
handler.sendEmptyMessage(MSG_DO_SOME_WORK);
}

View File

@ -19,6 +19,7 @@ import android.os.Handler;
import android.support.annotation.Nullable;
import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.upstream.TransferListener;
import com.google.android.exoplayer2.util.Assertions;
import java.util.ArrayList;
@ -34,9 +35,9 @@ public abstract class BaseMediaSource implements MediaSource {
private final ArrayList<SourceInfoRefreshListener> sourceInfoListeners;
private final MediaSourceEventListener.EventDispatcher eventDispatcher;
private ExoPlayer player;
private Timeline timeline;
private Object manifest;
private @Nullable ExoPlayer player;
private @Nullable Timeline timeline;
private @Nullable Object manifest;
public BaseMediaSource() {
sourceInfoListeners = new ArrayList<>(/* initialCapacity= */ 1);
@ -51,12 +52,19 @@ public abstract class BaseMediaSource implements MediaSource {
* @param isTopLevelSource Whether this source has been passed directly to {@link
* ExoPlayer#prepare(MediaSource)} or {@link ExoPlayer#prepare(MediaSource, boolean,
* boolean)}.
* @param mediaTransferListener The transfer listener which should be informed of any media data
* transfers. May be null if no listener is available. Note that this listener should usually
* be only informed of transfers related to the media loads and not of auxiliary loads for
* manifests and other data.
*/
protected abstract void prepareSourceInternal(ExoPlayer player, boolean isTopLevelSource);
protected abstract void prepareSourceInternal(
ExoPlayer player,
boolean isTopLevelSource,
@Nullable TransferListener<?> mediaTransferListener);
/**
* Releases the source. This method is called exactly once after each call to {@link
* #prepareSourceInternal(ExoPlayer, boolean)}.
* #prepareSourceInternal(ExoPlayer, boolean, TransferListener)}.
*/
protected abstract void releaseSourceInternal();
@ -129,12 +137,15 @@ public abstract class BaseMediaSource implements MediaSource {
@Override
public final void prepareSource(
ExoPlayer player, boolean isTopLevelSource, SourceInfoRefreshListener listener) {
ExoPlayer player,
boolean isTopLevelSource,
SourceInfoRefreshListener listener,
@Nullable TransferListener<?> mediaTransferListener) {
Assertions.checkArgument(this.player == null || this.player == player);
sourceInfoListeners.add(listener);
if (this.player == null) {
this.player = player;
prepareSourceInternal(player, isTopLevelSource);
prepareSourceInternal(player, isTopLevelSource, mediaTransferListener);
} else if (timeline != null) {
listener.onSourceInfoRefreshed(/* source= */ this, timeline, manifest);
}

View File

@ -21,6 +21,7 @@ import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.upstream.Allocator;
import com.google.android.exoplayer2.upstream.TransferListener;
import com.google.android.exoplayer2.util.Assertions;
import java.io.IOException;
import java.lang.annotation.Retention;
@ -211,8 +212,11 @@ public final class ClippingMediaSource extends CompositeMediaSource<Void> {
}
@Override
public void prepareSourceInternal(ExoPlayer player, boolean isTopLevelSource) {
super.prepareSourceInternal(player, isTopLevelSource);
public void prepareSourceInternal(
ExoPlayer player,
boolean isTopLevelSource,
@Nullable TransferListener<?> mediaTransferListener) {
super.prepareSourceInternal(player, isTopLevelSource, mediaTransferListener);
prepareChildSource(/* id= */ null, mediaSource);
}

View File

@ -21,6 +21,7 @@ import android.support.annotation.Nullable;
import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.source.MediaSourceEventListener.MediaLoadData;
import com.google.android.exoplayer2.upstream.TransferListener;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Util;
import java.io.IOException;
@ -37,6 +38,7 @@ public abstract class CompositeMediaSource<T> extends BaseMediaSource {
private @Nullable ExoPlayer player;
private @Nullable Handler eventHandler;
private @Nullable TransferListener<?> mediaTransferListener;
/** Create composite media source without child sources. */
protected CompositeMediaSource() {
@ -45,8 +47,12 @@ public abstract class CompositeMediaSource<T> extends BaseMediaSource {
@Override
@CallSuper
public void prepareSourceInternal(ExoPlayer player, boolean isTopLevelSource) {
public void prepareSourceInternal(
ExoPlayer player,
boolean isTopLevelSource,
@Nullable TransferListener<?> mediaTransferListener) {
this.player = player;
this.mediaTransferListener = mediaTransferListener;
eventHandler = new Handler();
}
@ -107,7 +113,10 @@ public abstract class CompositeMediaSource<T> extends BaseMediaSource {
childSources.put(id, new MediaSourceAndListener(mediaSource, sourceListener, eventListener));
mediaSource.addEventListener(Assertions.checkNotNull(eventHandler), eventListener);
mediaSource.prepareSource(
Assertions.checkNotNull(player), /* isTopLevelSource= */ false, sourceListener);
Assertions.checkNotNull(player),
/* isTopLevelSource= */ false,
sourceListener,
mediaTransferListener);
}
/**

View File

@ -26,6 +26,7 @@ import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.source.ConcatenatingMediaSource.MediaSourceHolder;
import com.google.android.exoplayer2.source.ShuffleOrder.DefaultShuffleOrder;
import com.google.android.exoplayer2.upstream.Allocator;
import com.google.android.exoplayer2.upstream.TransferListener;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Util;
import java.util.ArrayList;
@ -373,8 +374,11 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
}
@Override
public final synchronized void prepareSourceInternal(ExoPlayer player, boolean isTopLevelSource) {
super.prepareSourceInternal(player, isTopLevelSource);
public final synchronized void prepareSourceInternal(
ExoPlayer player,
boolean isTopLevelSource,
@Nullable TransferListener<?> mediaTransferListener) {
super.prepareSourceInternal(player, isTopLevelSource, mediaTransferListener);
this.player = player;
playerApplicationHandler = new Handler(player.getApplicationLooper());
if (mediaSourcesPublic.isEmpty()) {

View File

@ -27,6 +27,7 @@ import com.google.android.exoplayer2.extractor.ExtractorsFactory;
import com.google.android.exoplayer2.source.ads.AdsMediaSource;
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 java.io.IOException;
@ -344,7 +345,10 @@ public final class ExtractorMediaSource extends BaseMediaSource
}
@Override
public void prepareSourceInternal(ExoPlayer player, boolean isTopLevelSource) {
public void prepareSourceInternal(
ExoPlayer player,
boolean isTopLevelSource,
@Nullable TransferListener<?> mediaTransferListener) {
notifySourceInfoRefreshed(timelineDurationUs, /* isSeekable= */ false);
}

View File

@ -22,6 +22,7 @@ import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.source.ShuffleOrder.UnshuffledShuffleOrder;
import com.google.android.exoplayer2.upstream.Allocator;
import com.google.android.exoplayer2.upstream.TransferListener;
import com.google.android.exoplayer2.util.Assertions;
/**
@ -60,8 +61,11 @@ public final class LoopingMediaSource extends CompositeMediaSource<Void> {
}
@Override
public void prepareSourceInternal(ExoPlayer player, boolean isTopLevelSource) {
super.prepareSourceInternal(player, isTopLevelSource);
public void prepareSourceInternal(
ExoPlayer player,
boolean isTopLevelSource,
@Nullable TransferListener<?> mediaTransferListener) {
super.prepareSourceInternal(player, isTopLevelSource, mediaTransferListener);
prepareChildSource(/* id= */ null, childSource);
}

View File

@ -21,6 +21,7 @@ import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.upstream.Allocator;
import com.google.android.exoplayer2.upstream.TransferListener;
import java.io.IOException;
/**
@ -32,7 +33,7 @@ import java.io.IOException;
* provide a new timeline whenever the structure of the media changes. The MediaSource
* provides these timelines by calling {@link SourceInfoRefreshListener#onSourceInfoRefreshed}
* on the {@link SourceInfoRefreshListener}s passed to {@link #prepareSource(ExoPlayer,
* boolean, SourceInfoRefreshListener)}.
* boolean, SourceInfoRefreshListener, TransferListener)}.
* <li>To provide {@link MediaPeriod} instances for the periods in its timeline. MediaPeriods are
* obtained by calling {@link #createPeriod(MediaPeriodId, Allocator)}, and provide a way for
* the player to load and read the media.
@ -206,9 +207,16 @@ public interface MediaSource {
* boolean)}. If {@code false}, this source is being prepared by another source (e.g. {@link
* ConcatenatingMediaSource}) for composition.
* @param listener The listener to be added.
* @param mediaTransferListener The transfer listener which should be informed of any media data
* transfers. May be null if no listener is available. Note that this listener should be only
* informed of transfers related to the media loads and not of auxiliary loads for manifests
* and other data.
*/
void prepareSource(
ExoPlayer player, boolean isTopLevelSource, SourceInfoRefreshListener listener);
ExoPlayer player,
boolean isTopLevelSource,
SourceInfoRefreshListener listener,
@Nullable TransferListener<?> mediaTransferListener);
/**
* Throws any pending error encountered while loading or refreshing source information.

View File

@ -20,6 +20,7 @@ import android.support.annotation.Nullable;
import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.upstream.Allocator;
import com.google.android.exoplayer2.upstream.TransferListener;
import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@ -97,8 +98,11 @@ public final class MergingMediaSource extends CompositeMediaSource<Integer> {
}
@Override
public void prepareSourceInternal(ExoPlayer player, boolean isTopLevelSource) {
super.prepareSourceInternal(player, isTopLevelSource);
public void prepareSourceInternal(
ExoPlayer player,
boolean isTopLevelSource,
@Nullable TransferListener<?> mediaTransferListener) {
super.prepareSourceInternal(player, isTopLevelSource, mediaTransferListener);
for (int i = 0; i < mediaSources.length; i++) {
prepareChildSource(i, mediaSources[i]);
}

View File

@ -24,6 +24,7 @@ import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.upstream.Allocator;
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.Assertions;
import java.io.IOException;
@ -268,7 +269,10 @@ public final class SingleSampleMediaSource extends BaseMediaSource {
// MediaSource implementation.
@Override
public void prepareSourceInternal(ExoPlayer player, boolean isTopLevelSource) {
public void prepareSourceInternal(
ExoPlayer player,
boolean isTopLevelSource,
@Nullable TransferListener<?> mediaTransferListener) {
refreshSourceInfo(timeline, /* manifest= */ null);
}

View File

@ -36,6 +36,7 @@ import com.google.android.exoplayer2.source.MediaSourceEventListener.MediaLoadDa
import com.google.android.exoplayer2.upstream.Allocator;
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.Assertions;
import java.io.IOException;
import java.lang.annotation.Retention;
@ -305,8 +306,11 @@ public final class AdsMediaSource extends CompositeMediaSource<MediaPeriodId> {
}
@Override
public void prepareSourceInternal(final ExoPlayer player, boolean isTopLevelSource) {
super.prepareSourceInternal(player, isTopLevelSource);
public void prepareSourceInternal(
final ExoPlayer player,
boolean isTopLevelSource,
@Nullable TransferListener<?> mediaTransferListener) {
super.prepareSourceInternal(player, isTopLevelSource, mediaTransferListener);
Assertions.checkArgument(isTopLevelSource);
final ComponentListener componentListener = new ComponentListener();
this.componentListener = componentListener;

View File

@ -18,6 +18,7 @@ package com.google.android.exoplayer2;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.fail;
import android.support.annotation.Nullable;
import android.view.Surface;
import com.google.android.exoplayer2.Player.EventListener;
import com.google.android.exoplayer2.Timeline.Window;
@ -44,6 +45,7 @@ import com.google.android.exoplayer2.testutil.FakeTrackSelection;
import com.google.android.exoplayer2.testutil.FakeTrackSelector;
import com.google.android.exoplayer2.testutil.RobolectricUtil;
import com.google.android.exoplayer2.upstream.Allocator;
import com.google.android.exoplayer2.upstream.TransferListener;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
@ -229,8 +231,10 @@ public final class ExoPlayerTest {
new FakeMediaSource(timeline, new Object(), Builder.VIDEO_FORMAT) {
@Override
public synchronized void prepareSourceInternal(
ExoPlayer player, boolean isTopLevelSource) {
super.prepareSourceInternal(player, isTopLevelSource);
ExoPlayer player,
boolean isTopLevelSource,
@Nullable TransferListener<?> mediaTransferListener) {
super.prepareSourceInternal(player, isTopLevelSource, mediaTransferListener);
// We've queued a source info refresh on the playback thread's event queue. Allow the
// test thread to prepare the player with the third source, and block this thread (the
// playback thread) until the test thread's call to prepare() has returned.

View File

@ -46,6 +46,7 @@ import com.google.android.exoplayer2.upstream.Loader;
import com.google.android.exoplayer2.upstream.Loader.LoadErrorAction;
import com.google.android.exoplayer2.upstream.LoaderErrorThrower;
import com.google.android.exoplayer2.upstream.ParsingLoadable;
import com.google.android.exoplayer2.upstream.TransferListener;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Util;
import java.io.BufferedReader;
@ -544,7 +545,10 @@ public final class DashMediaSource extends BaseMediaSource {
// MediaSource implementation.
@Override
public void prepareSourceInternal(ExoPlayer player, boolean isTopLevelSource) {
public void prepareSourceInternal(
ExoPlayer player,
boolean isTopLevelSource,
@Nullable TransferListener<?> mediaTransferListener) {
if (sideloadedManifest) {
processManifest(false);
} else {

View File

@ -40,6 +40,7 @@ import com.google.android.exoplayer2.source.hls.playlist.HlsPlaylistTracker;
import com.google.android.exoplayer2.upstream.Allocator;
import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.ParsingLoadable;
import com.google.android.exoplayer2.upstream.TransferListener;
import com.google.android.exoplayer2.util.Assertions;
import java.io.IOException;
import java.util.List;
@ -367,7 +368,10 @@ public final class HlsMediaSource extends BaseMediaSource
}
@Override
public void prepareSourceInternal(ExoPlayer player, boolean isTopLevelSource) {
public void prepareSourceInternal(
ExoPlayer player,
boolean isTopLevelSource,
@Nullable TransferListener<?> mediaTransferListener) {
EventDispatcher eventDispatcher = createEventDispatcher(/* mediaPeriodId= */ null);
playlistTracker.start(manifestUri, eventDispatcher, /* listener= */ this);
}

View File

@ -44,6 +44,7 @@ import com.google.android.exoplayer2.upstream.Loader;
import com.google.android.exoplayer2.upstream.Loader.LoadErrorAction;
import com.google.android.exoplayer2.upstream.LoaderErrorThrower;
import com.google.android.exoplayer2.upstream.ParsingLoadable;
import com.google.android.exoplayer2.upstream.TransferListener;
import com.google.android.exoplayer2.util.Assertions;
import java.io.IOException;
import java.util.ArrayList;
@ -462,7 +463,10 @@ public final class SsMediaSource extends BaseMediaSource
// MediaSource implementation.
@Override
public void prepareSourceInternal(ExoPlayer player, boolean isTopLevelSource) {
public void prepareSourceInternal(
ExoPlayer player,
boolean isTopLevelSource,
@Nullable TransferListener<?> mediaTransferListener) {
if (sideloadedManifest) {
manifestLoaderErrorThrower = new LoaderErrorThrower.Dummy();
processManifest();

View File

@ -36,6 +36,7 @@ import com.google.android.exoplayer2.source.TrackGroup;
import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.upstream.Allocator;
import com.google.android.exoplayer2.upstream.DataSpec;
import com.google.android.exoplayer2.upstream.TransferListener;
import com.google.android.exoplayer2.util.Assertions;
import java.io.IOException;
import java.util.ArrayList;
@ -86,7 +87,10 @@ public class FakeMediaSource extends BaseMediaSource {
}
@Override
public synchronized void prepareSourceInternal(ExoPlayer player, boolean isTopLevelSource) {
public synchronized void prepareSourceInternal(
ExoPlayer player,
boolean isTopLevelSource,
@Nullable TransferListener<?> mediaTransferListener) {
assertThat(preparedSource).isFalse();
preparedSource = true;
releasedSource = false;

View File

@ -126,7 +126,11 @@ public class MediaSourceTestRunner {
new Runnable() {
@Override
public void run() {
mediaSource.prepareSource(player, true, mediaSourceListener);
mediaSource.prepareSource(
player,
/* isTopLevelSource= */ true,
mediaSourceListener,
/* mediaTransferListener= */ null);
try {
// TODO: This only catches errors that are set synchronously in prepareSource. To
// capture async errors we'll need to poll maybeThrowSourceInfoRefreshError until the