Make AdsMediaSource reusable.

Also fixes a bug where deferred media periods were kept in the list for
an unprepared media source although the media period was already released.

Issue:#3498

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=183800889
This commit is contained in:
tonihei 2018-01-30 02:21:18 -08:00 committed by Oliver Woodman
parent 6709dc7f64
commit 17edae6528

View file

@ -102,14 +102,11 @@ public final class AdsMediaSource extends CompositeMediaSource<MediaPeriodId> {
@Nullable private final Handler eventHandler;
@Nullable private final EventListener eventListener;
private final Handler mainHandler;
private final ComponentListener componentListener;
private final Map<MediaSource, List<DeferredMediaPeriod>> deferredMediaPeriodByAdMediaSource;
private final Timeline.Period period;
private Handler playerHandler;
private volatile boolean released;
// Accessed on the player thread.
private ComponentListener componentListener;
private Timeline contentTimeline;
private Object contentManifest;
private AdPlaybackState adPlaybackState;
@ -192,7 +189,6 @@ public final class AdsMediaSource extends CompositeMediaSource<MediaPeriodId> {
this.eventHandler = eventHandler;
this.eventListener = eventListener;
mainHandler = new Handler(Looper.getMainLooper());
componentListener = new ComponentListener();
deferredMediaPeriodByAdMediaSource = new HashMap<>();
period = new Timeline.Period();
adGroupMediaSources = new MediaSource[0][];
@ -204,9 +200,9 @@ public final class AdsMediaSource extends CompositeMediaSource<MediaPeriodId> {
public void prepareSource(final ExoPlayer player, boolean isTopLevelSource, Listener listener) {
super.prepareSource(player, isTopLevelSource, listener);
Assertions.checkArgument(isTopLevelSource);
Assertions.checkState(this.listener == null, MEDIA_SOURCE_REUSED_ERROR_MESSAGE);
final ComponentListener componentListener = new ComponentListener();
this.listener = listener;
playerHandler = new Handler();
this.componentListener = componentListener;
prepareChildSource(new MediaPeriodId(/* periodIndex= */ 0), contentMediaSource);
mainHandler.post(new Runnable() {
@Override
@ -258,13 +254,27 @@ public final class AdsMediaSource extends CompositeMediaSource<MediaPeriodId> {
@Override
public void releasePeriod(MediaPeriod mediaPeriod) {
((DeferredMediaPeriod) mediaPeriod).releasePeriod();
DeferredMediaPeriod deferredMediaPeriod = (DeferredMediaPeriod) mediaPeriod;
List<DeferredMediaPeriod> mediaPeriods =
deferredMediaPeriodByAdMediaSource.get(deferredMediaPeriod.mediaSource);
if (mediaPeriods != null) {
mediaPeriods.remove(deferredMediaPeriod);
}
deferredMediaPeriod.releasePeriod();
}
@Override
public void releaseSource() {
super.releaseSource();
released = true;
componentListener.release();
componentListener = null;
deferredMediaPeriodByAdMediaSource.clear();
contentTimeline = null;
contentManifest = null;
adPlaybackState = null;
adGroupMediaSources = new MediaSource[0][];
adDurationsUs = new long[0][];
listener = null;
mainHandler.post(new Runnable() {
@Override
public void run() {
@ -301,20 +311,6 @@ public final class AdsMediaSource extends CompositeMediaSource<MediaPeriodId> {
maybeUpdateSourceInfo();
}
private void onLoadError(final IOException error) {
Log.w(TAG, "Ad load error", error);
if (eventHandler != null && eventListener != null) {
eventHandler.post(new Runnable() {
@Override
public void run() {
if (!released) {
eventListener.onAdLoadError(error);
}
}
});
}
}
private void onContentSourceInfoRefreshed(Timeline timeline, Object manifest) {
contentTimeline = timeline;
contentManifest = manifest;
@ -349,6 +345,23 @@ public final class AdsMediaSource extends CompositeMediaSource<MediaPeriodId> {
/** Listener for component events. All methods are called on the main thread. */
private final class ComponentListener implements AdsLoader.EventListener {
private final Handler playerHandler;
private volatile boolean released;
/**
* Creates new listener which forwards ad playback states on the creating thread and all other
* events on the external event listener thread.
*/
public ComponentListener() {
playerHandler = new Handler();
}
/** Releases the component listener. */
public void release() {
released = true;
playerHandler.removeCallbacksAndMessages(null);
}
@Override
public void onAdPlaybackState(final AdPlaybackState adPlaybackState) {
if (released) {
@ -367,6 +380,9 @@ public final class AdsMediaSource extends CompositeMediaSource<MediaPeriodId> {
@Override
public void onAdClicked() {
if (released) {
return;
}
if (eventHandler != null && eventListener != null) {
eventHandler.post(new Runnable() {
@Override
@ -381,6 +397,9 @@ public final class AdsMediaSource extends CompositeMediaSource<MediaPeriodId> {
@Override
public void onAdTapped() {
if (released) {
return;
}
if (eventHandler != null && eventListener != null) {
eventHandler.post(new Runnable() {
@Override
@ -398,15 +417,18 @@ public final class AdsMediaSource extends CompositeMediaSource<MediaPeriodId> {
if (released) {
return;
}
playerHandler.post(new Runnable() {
@Override
public void run() {
if (released) {
return;
}
AdsMediaSource.this.onLoadError(error);
}
});
Log.w(TAG, "Ad load error", error);
if (eventHandler != null && eventListener != null) {
eventHandler.post(
new Runnable() {
@Override
public void run() {
if (!released) {
eventListener.onAdLoadError(error);
}
}
});
}
}
}