Commit dc4148d5 by bachinger Committed by Oliver Woodman

Add positions and new reasons to onPositionDiscontinuity

PiperOrigin-RevId: 364861539
parent f19ab4aa
Showing with 458 additions and 105 deletions
...@@ -2,6 +2,17 @@ ...@@ -2,6 +2,17 @@
### dev-v2 (not yet released) ### dev-v2 (not yet released)
* Core Library:
* Add position info of the old and the new position as arguments to
`EventListener.onPositionDiscontinuity`. Add the new reasons
`DISCONTINUITY_REASON_SKIP` and `DISCONTINUITY_REASON_REMOVE` and rename
`DISCONTINUITY_REASON_PERIOD_TRANSITION` to
`DISCONTINUITY_REASON_AUTO_TRANSITION`. Remove
`DISCONTINUITY_REASON_AD_INSERTION` for which
`DISCONTINUITY_REASON_AUTO_TRANSITION` is used instead. Deprecate the
`onPositionDiscontinuity(int)` callback
([#6163](https://github.com/google/ExoPlayer/issues/6163),
[#4768](https://github.com/google/ExoPlayer/issues/4768)).
* UI: * UI:
* Add builder for `PlayerNotificationManager`. * Add builder for `PlayerNotificationManager`.
* Add group setting to `PlayerNotificationManager`. * Add group setting to `PlayerNotificationManager`.
......
...@@ -460,6 +460,7 @@ public final class CastPlayer extends BasePlayer { ...@@ -460,6 +460,7 @@ public final class CastPlayer extends BasePlayer {
pendingSeekCount++; pendingSeekCount++;
pendingSeekWindowIndex = windowIndex; pendingSeekWindowIndex = windowIndex;
pendingSeekPositionMs = positionMs; pendingSeekPositionMs = positionMs;
// TODO(b/181262841): call new onPositionDiscontinuity callback
listeners.queueEvent( listeners.queueEvent(
Player.EVENT_POSITION_DISCONTINUITY, Player.EVENT_POSITION_DISCONTINUITY,
listener -> listener.onPositionDiscontinuity(DISCONTINUITY_REASON_SEEK)); listener -> listener.onPositionDiscontinuity(DISCONTINUITY_REASON_SEEK));
...@@ -630,6 +631,8 @@ public final class CastPlayer extends BasePlayer { ...@@ -630,6 +631,8 @@ public final class CastPlayer extends BasePlayer {
// Internal methods. // Internal methods.
// Call deprecated callbacks.
@SuppressWarnings("deprecation")
private void updateInternalStateAndNotifyIfChanged() { private void updateInternalStateAndNotifyIfChanged() {
if (remoteMediaClient == null) { if (remoteMediaClient == null) {
// There is no session. We leave the state of the player as it is now. // There is no session. We leave the state of the player as it is now.
...@@ -648,9 +651,10 @@ public final class CastPlayer extends BasePlayer { ...@@ -648,9 +651,10 @@ public final class CastPlayer extends BasePlayer {
int currentWindowIndex = fetchCurrentWindowIndex(remoteMediaClient, currentTimeline); int currentWindowIndex = fetchCurrentWindowIndex(remoteMediaClient, currentTimeline);
if (this.currentWindowIndex != currentWindowIndex && pendingSeekCount == 0) { if (this.currentWindowIndex != currentWindowIndex && pendingSeekCount == 0) {
this.currentWindowIndex = currentWindowIndex; this.currentWindowIndex = currentWindowIndex;
// TODO(b/181262841): call new onPositionDiscontinuity callback
listeners.queueEvent( listeners.queueEvent(
Player.EVENT_POSITION_DISCONTINUITY, Player.EVENT_POSITION_DISCONTINUITY,
listener -> listener.onPositionDiscontinuity(DISCONTINUITY_REASON_PERIOD_TRANSITION)); listener -> listener.onPositionDiscontinuity(DISCONTINUITY_REASON_AUTO_TRANSITION));
listeners.queueEvent( listeners.queueEvent(
Player.EVENT_MEDIA_ITEM_TRANSITION, Player.EVENT_MEDIA_ITEM_TRANSITION,
listener -> listener ->
......
...@@ -468,7 +468,10 @@ import java.util.Map; ...@@ -468,7 +468,10 @@ import java.util.Map;
} }
@Override @Override
public void onPositionDiscontinuity(@Player.DiscontinuityReason int reason) { public void onPositionDiscontinuity(
Player.PositionInfo oldPosition,
Player.PositionInfo newPosition,
@Player.DiscontinuityReason int reason) {
handleTimelineOrPositionChanged(); handleTimelineOrPositionChanged();
} }
......
...@@ -613,7 +613,10 @@ public final class ImaAdsLoader implements Player.EventListener, AdsLoader { ...@@ -613,7 +613,10 @@ public final class ImaAdsLoader implements Player.EventListener, AdsLoader {
} }
@Override @Override
public void onPositionDiscontinuity(@Player.DiscontinuityReason int reason) { public void onPositionDiscontinuity(
Player.PositionInfo oldPosition,
Player.PositionInfo newPosition,
@Player.DiscontinuityReason int reason) {
maybeUpdateCurrentAdTagLoader(); maybeUpdateCurrentAdTagLoader();
maybePreloadNextPeriodAds(); maybePreloadNextPeriodAds();
} }
......
...@@ -29,6 +29,8 @@ import com.google.android.exoplayer2.util.ListenerSet; ...@@ -29,6 +29,8 @@ import com.google.android.exoplayer2.util.ListenerSet;
private final ListenerSet<EventListener> listeners; private final ListenerSet<EventListener> listeners;
private final Timeline.Period period; private final Timeline.Period period;
private final Object windowUid = new Object();
private final Object periodUid = new Object();
private Timeline timeline; private Timeline timeline;
@Player.State private int state; @Player.State private int state;
...@@ -65,6 +67,16 @@ import com.google.android.exoplayer2.util.ListenerSet; ...@@ -65,6 +67,16 @@ import com.google.android.exoplayer2.util.ListenerSet;
*/ */
public void setPlayingContentPosition(int periodIndex, long positionMs) { public void setPlayingContentPosition(int periodIndex, long positionMs) {
boolean notify = isPlayingAd; boolean notify = isPlayingAd;
PositionInfo oldPosition =
new PositionInfo(
windowUid,
/* windowIndex= */ 0,
periodUid,
/* periodIndex= */ 0,
this.positionMs,
this.contentPositionMs,
this.adGroupIndex,
this.adIndexInAdGroup);
isPlayingAd = false; isPlayingAd = false;
adGroupIndex = C.INDEX_UNSET; adGroupIndex = C.INDEX_UNSET;
adIndexInAdGroup = C.INDEX_UNSET; adIndexInAdGroup = C.INDEX_UNSET;
...@@ -72,9 +84,21 @@ import com.google.android.exoplayer2.util.ListenerSet; ...@@ -72,9 +84,21 @@ import com.google.android.exoplayer2.util.ListenerSet;
this.positionMs = positionMs; this.positionMs = positionMs;
contentPositionMs = positionMs; contentPositionMs = positionMs;
if (notify) { if (notify) {
PositionInfo newPosition =
new PositionInfo(
windowUid,
/* windowIndex= */ 0,
periodUid,
/* periodIndex= */ 0,
positionMs,
this.contentPositionMs,
this.adGroupIndex,
this.adIndexInAdGroup);
listeners.sendEvent( listeners.sendEvent(
Player.EVENT_POSITION_DISCONTINUITY, Player.EVENT_POSITION_DISCONTINUITY,
listener -> listener.onPositionDiscontinuity(DISCONTINUITY_REASON_AD_INSERTION)); listener ->
listener.onPositionDiscontinuity(
oldPosition, newPosition, DISCONTINUITY_REASON_AUTO_TRANSITION));
} }
} }
...@@ -90,6 +114,16 @@ import com.google.android.exoplayer2.util.ListenerSet; ...@@ -90,6 +114,16 @@ import com.google.android.exoplayer2.util.ListenerSet;
long positionMs, long positionMs,
long contentPositionMs) { long contentPositionMs) {
boolean notify = !isPlayingAd || this.adIndexInAdGroup != adIndexInAdGroup; boolean notify = !isPlayingAd || this.adIndexInAdGroup != adIndexInAdGroup;
PositionInfo oldPosition =
new PositionInfo(
windowUid,
/* windowIndex= */ 0,
periodUid,
/* periodIndex= */ 0,
this.positionMs,
this.contentPositionMs,
this.adGroupIndex,
this.adIndexInAdGroup);
isPlayingAd = true; isPlayingAd = true;
this.periodIndex = periodIndex; this.periodIndex = periodIndex;
this.adGroupIndex = adGroupIndex; this.adGroupIndex = adGroupIndex;
...@@ -97,9 +131,21 @@ import com.google.android.exoplayer2.util.ListenerSet; ...@@ -97,9 +131,21 @@ import com.google.android.exoplayer2.util.ListenerSet;
this.positionMs = positionMs; this.positionMs = positionMs;
this.contentPositionMs = contentPositionMs; this.contentPositionMs = contentPositionMs;
if (notify) { if (notify) {
PositionInfo newPosition =
new PositionInfo(
windowUid,
/* windowIndex= */ 0,
periodUid,
/* periodIndex= */ 0,
positionMs,
contentPositionMs,
adGroupIndex,
adIndexInAdGroup);
listeners.sendEvent( listeners.sendEvent(
EVENT_POSITION_DISCONTINUITY, EVENT_POSITION_DISCONTINUITY,
listener -> listener.onPositionDiscontinuity(DISCONTINUITY_REASON_AD_INSERTION)); listener ->
listener.onPositionDiscontinuity(
oldPosition, newPosition, DISCONTINUITY_REASON_AUTO_TRANSITION));
} }
} }
......
...@@ -281,7 +281,26 @@ public final class ImaAdsLoaderTest { ...@@ -281,7 +281,26 @@ public final class ImaAdsLoaderTest {
videoAdPlayer.pauseAd(TEST_AD_MEDIA_INFO); videoAdPlayer.pauseAd(TEST_AD_MEDIA_INFO);
videoAdPlayer.stopAd(TEST_AD_MEDIA_INFO); videoAdPlayer.stopAd(TEST_AD_MEDIA_INFO);
imaAdsLoader.onPlayerError(ExoPlaybackException.createForSource(new IOException())); imaAdsLoader.onPlayerError(ExoPlaybackException.createForSource(new IOException()));
imaAdsLoader.onPositionDiscontinuity(Player.DISCONTINUITY_REASON_SEEK); imaAdsLoader.onPositionDiscontinuity(
new Player.PositionInfo(
/* windowUid= */ new Object(),
/* windowIndex= */ 0,
/* periodUid= */ new Object(),
/* periodIndex= */ 0,
/* positionMs= */ 10_000,
/* contentPositionMs= */ 0,
/* adGroupIndex= */ -1,
/* adIndexInAdGroup= */ -1),
new Player.PositionInfo(
/* windowUid= */ new Object(),
/* windowIndex= */ 1,
/* periodUid= */ new Object(),
/* periodIndex= */ 0,
/* positionMs= */ 20_000,
/* contentPositionMs= */ 0,
/* adGroupIndex= */ -1,
/* adIndexInAdGroup= */ -1),
Player.DISCONTINUITY_REASON_SEEK);
adEventListener.onAdEvent(getAdEvent(AdEventType.CONTENT_RESUME_REQUESTED, /* ad= */ null)); adEventListener.onAdEvent(getAdEvent(AdEventType.CONTENT_RESUME_REQUESTED, /* ad= */ null));
imaAdsLoader.handlePrepareError( imaAdsLoader.handlePrepareError(
adsMediaSource, /* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 0, new IOException()); adsMediaSource, /* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 0, new IOException());
......
...@@ -306,7 +306,10 @@ public final class LeanbackPlayerAdapter extends PlayerAdapter implements Runnab ...@@ -306,7 +306,10 @@ public final class LeanbackPlayerAdapter extends PlayerAdapter implements Runnab
} }
@Override @Override
public void onPositionDiscontinuity(@DiscontinuityReason int reason) { public void onPositionDiscontinuity(
Player.PositionInfo oldPosition,
Player.PositionInfo newPosition,
@DiscontinuityReason int reason) {
Callback callback = getCallback(); Callback callback = getCallback();
callback.onCurrentPositionChanged(LeanbackPlayerAdapter.this); callback.onCurrentPositionChanged(LeanbackPlayerAdapter.this);
callback.onBufferedPositionChanged(LeanbackPlayerAdapter.this); callback.onBufferedPositionChanged(LeanbackPlayerAdapter.this);
......
...@@ -437,7 +437,7 @@ import java.util.List; ...@@ -437,7 +437,7 @@ import java.util.List;
case Player.STATE_READY: case Player.STATE_READY:
if (!prepared) { if (!prepared) {
prepared = true; prepared = true;
handlePositionDiscontinuity(Player.DISCONTINUITY_REASON_PERIOD_TRANSITION); handlePositionDiscontinuity(Player.DISCONTINUITY_REASON_AUTO_TRANSITION);
listener.onPrepared( listener.onPrepared(
Assertions.checkNotNull(getCurrentMediaItem()), player.getBufferedPercentage()); Assertions.checkNotNull(getCurrentMediaItem()), player.getBufferedPercentage());
} }
...@@ -517,9 +517,11 @@ import java.util.List; ...@@ -517,9 +517,11 @@ import java.util.List;
int currentWindowIndex = getCurrentMediaItemIndex(); int currentWindowIndex = getCurrentMediaItemIndex();
if (this.currentWindowIndex != currentWindowIndex) { if (this.currentWindowIndex != currentWindowIndex) {
this.currentWindowIndex = currentWindowIndex; this.currentWindowIndex = currentWindowIndex;
androidx.media2.common.MediaItem currentMediaItem = if (currentWindowIndex != C.INDEX_UNSET) {
Assertions.checkNotNull(getCurrentMediaItem()); androidx.media2.common.MediaItem currentMediaItem =
listener.onCurrentMediaItemChanged(currentMediaItem); Assertions.checkNotNull(getCurrentMediaItem());
listener.onCurrentMediaItemChanged(currentMediaItem);
}
} else { } else {
listener.onSeekCompleted(); listener.onSeekCompleted();
} }
...@@ -597,7 +599,10 @@ import java.util.List; ...@@ -597,7 +599,10 @@ import java.util.List;
} }
@Override @Override
public void onPositionDiscontinuity(@Player.DiscontinuityReason int reason) { public void onPositionDiscontinuity(
Player.PositionInfo oldPosition,
Player.PositionInfo newPosition,
@Player.DiscontinuityReason int reason) {
handlePositionDiscontinuity(reason); handlePositionDiscontinuity(reason);
} }
......
...@@ -15,7 +15,6 @@ ...@@ -15,7 +15,6 @@
*/ */
package com.google.android.exoplayer2; package com.google.android.exoplayer2;
import android.content.Context; import android.content.Context;
import android.os.Looper; import android.os.Looper;
import android.view.Surface; import android.view.Surface;
...@@ -40,6 +39,7 @@ import com.google.android.exoplayer2.util.Util; ...@@ -40,6 +39,7 @@ import com.google.android.exoplayer2.util.Util;
import com.google.android.exoplayer2.video.VideoFrameMetadataListener; import com.google.android.exoplayer2.video.VideoFrameMetadataListener;
import com.google.android.exoplayer2.video.VideoListener; import com.google.android.exoplayer2.video.VideoListener;
import com.google.android.exoplayer2.video.spherical.CameraMotionListener; import com.google.android.exoplayer2.video.spherical.CameraMotionListener;
import com.google.common.base.Objects;
import java.lang.annotation.Documented; import java.lang.annotation.Documented;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
...@@ -396,10 +396,9 @@ public interface Player { ...@@ -396,10 +396,9 @@ public interface Player {
/** /**
* Called when the timeline has been refreshed. * Called when the timeline has been refreshed.
* *
* <p>Note that if the timeline has changed then a position discontinuity may also have * <p>Note that the current window or period index may change as a result of a timeline change.
* occurred. For example, the current period index may have changed as a result of periods being * If playback can't continue smoothly because of this timeline change, a separate {@link
* added or removed from the timeline. This will <em>not</em> be reported via a separate call to * #onPositionDiscontinuity(PositionInfo, PositionInfo, int)} callback will be triggered.
* {@link #onPositionDiscontinuity(int)}.
* *
* <p>{@link #onEvents(Player, Events)} will also be called to report this event along with * <p>{@link #onEvents(Player, Events)} will also be called to report this event along with
* other events that happen in the same {@link Looper} message queue iteration. * other events that happen in the same {@link Looper} message queue iteration.
...@@ -576,21 +575,27 @@ public interface Player { ...@@ -576,21 +575,27 @@ public interface Player {
default void onPlayerError(ExoPlaybackException error) {} default void onPlayerError(ExoPlaybackException error) {}
/** /**
* Called when a position discontinuity occurs without a change to the timeline. A position * @deprecated Use {@link #onPositionDiscontinuity(PositionInfo, PositionInfo, int)} instead.
* discontinuity occurs when the current window or period index changes (as a result of playback */
* transitioning from one period in the timeline to the next), or when the playback position @Deprecated
* jumps within the period currently being played (as a result of a seek being performed, or default void onPositionDiscontinuity(@DiscontinuityReason int reason) {}
* when the source introduces a discontinuity internally).
/**
* Called when a position discontinuity occurs.
* *
* <p>When a position discontinuity occurs as a result of a change to the timeline this method * <p>A position discontinuity occurs when the playing period changes, the playback position
* is <em>not</em> called. {@link #onTimelineChanged(Timeline, int)} is called in this case. * jumps within the period currently being played, or when the playing period has been skipped
* or removed.
* *
* <p>{@link #onEvents(Player, Events)} will also be called to report this event along with * <p>{@link #onEvents(Player, Events)} will also be called to report this event along with
* other events that happen in the same {@link Looper} message queue iteration. * other events that happen in the same {@link Looper} message queue iteration.
* *
* @param oldPosition The position before the discontinuity.
* @param newPosition The position after the discontinuity.
* @param reason The {@link DiscontinuityReason} responsible for the discontinuity. * @param reason The {@link DiscontinuityReason} responsible for the discontinuity.
*/ */
default void onPositionDiscontinuity(@DiscontinuityReason int reason) {} default void onPositionDiscontinuity(
PositionInfo oldPosition, PositionInfo newPosition, @DiscontinuityReason int reason) {}
/** /**
* Called when the current playback parameters change. The playback parameters may change due to * Called when the current playback parameters change. The playback parameters may change due to
...@@ -607,7 +612,8 @@ public interface Player { ...@@ -607,7 +612,8 @@ public interface Player {
/** /**
* @deprecated Seeks are processed without delay. Listen to {@link * @deprecated Seeks are processed without delay. Listen to {@link
* #onPositionDiscontinuity(int)} with reason {@link #DISCONTINUITY_REASON_SEEK} instead. * #onPositionDiscontinuity(PositionInfo, PositionInfo, int)} with reason {@link
* #DISCONTINUITY_REASON_SEEK} instead.
*/ */
@Deprecated @Deprecated
default void onSeekProcessed() {} default void onSeekProcessed() {}
...@@ -698,6 +704,94 @@ public interface Player { ...@@ -698,6 +704,94 @@ public interface Player {
} }
} }
/** Position info describing a playback position involved in a discontinuity. */
final class PositionInfo {
/**
* The UID of the window, or {@code null}, if the timeline is {@link Timeline#isEmpty() empty}.
*/
@Nullable public final Object windowUid;
/** The window index. */
public final int windowIndex;
/**
* The UID of the period, or {@code null}, if the timeline is {@link Timeline#isEmpty() empty}.
*/
@Nullable public final Object periodUid;
/** The period index. */
public final int periodIndex;
/** The playback position, in milliseconds. */
public final long positionMs;
/**
* The content position, in milliseconds.
*
* <p>If {@link #adGroupIndex} is {@link C#INDEX_UNSET}, this is the same as {@link
* #positionMs}.
*/
public final long contentPositionMs;
/**
* The ad group index if the playback position is within an ad, {@link C#INDEX_UNSET} otherwise.
*/
public final int adGroupIndex;
/**
* The index of the ad within the ad group if the playback position is within an ad, {@link
* C#INDEX_UNSET} otherwise.
*/
public final int adIndexInAdGroup;
/** Creates an instance. */
public PositionInfo(
@Nullable Object windowUid,
int windowIndex,
@Nullable Object periodUid,
int periodIndex,
long positionMs,
long contentPositionMs,
int adGroupIndex,
int adIndexInAdGroup) {
this.windowUid = windowUid;
this.windowIndex = windowIndex;
this.periodUid = periodUid;
this.periodIndex = periodIndex;
this.positionMs = positionMs;
this.contentPositionMs = contentPositionMs;
this.adGroupIndex = adGroupIndex;
this.adIndexInAdGroup = adIndexInAdGroup;
}
@Override
public boolean equals(@Nullable Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
PositionInfo that = (PositionInfo) o;
return windowIndex == that.windowIndex
&& periodIndex == that.periodIndex
&& positionMs == that.positionMs
&& contentPositionMs == that.contentPositionMs
&& adGroupIndex == that.adGroupIndex
&& adIndexInAdGroup == that.adIndexInAdGroup
&& Objects.equal(windowUid, that.windowUid)
&& Objects.equal(periodUid, that.periodUid);
}
@Override
public int hashCode() {
return Objects.hashCode(
windowUid,
windowIndex,
periodUid,
periodIndex,
windowIndex,
positionMs,
contentPositionMs,
adGroupIndex,
adIndexInAdGroup);
}
}
/** /**
* A set of {@link Command commands}. * A set of {@link Command commands}.
* *
...@@ -933,25 +1027,30 @@ public interface Player { ...@@ -933,25 +1027,30 @@ public interface Player {
int REPEAT_MODE_ALL = 2; int REPEAT_MODE_ALL = 2;
/** /**
* Reasons for position discontinuities. One of {@link #DISCONTINUITY_REASON_PERIOD_TRANSITION}, * Reasons for position discontinuities. One of {@link #DISCONTINUITY_REASON_AUTO_TRANSITION},
* {@link #DISCONTINUITY_REASON_SEEK}, {@link #DISCONTINUITY_REASON_SEEK_ADJUSTMENT}, {@link * {@link #DISCONTINUITY_REASON_SEEK}, {@link #DISCONTINUITY_REASON_SEEK_ADJUSTMENT}, {@link
* #DISCONTINUITY_REASON_AD_INSERTION} or {@link #DISCONTINUITY_REASON_INTERNAL}. * #DISCONTINUITY_REASON_SKIP}, {@link #DISCONTINUITY_REASON_REMOVE} or {@link
* #DISCONTINUITY_REASON_INTERNAL}.
*/ */
@Documented @Documented
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@IntDef({ @IntDef({
DISCONTINUITY_REASON_PERIOD_TRANSITION, DISCONTINUITY_REASON_AUTO_TRANSITION,
DISCONTINUITY_REASON_SEEK, DISCONTINUITY_REASON_SEEK,
DISCONTINUITY_REASON_SEEK_ADJUSTMENT, DISCONTINUITY_REASON_SEEK_ADJUSTMENT,
DISCONTINUITY_REASON_AD_INSERTION, DISCONTINUITY_REASON_SKIP,
DISCONTINUITY_REASON_REMOVE,
DISCONTINUITY_REASON_INTERNAL DISCONTINUITY_REASON_INTERNAL
}) })
@interface DiscontinuityReason {} @interface DiscontinuityReason {}
/** /**
* Automatic playback transition from one period in the timeline to the next. The period index may * Automatic playback transition from one period in the timeline to the next. The period index may
* be the same as it was before the discontinuity in case the current period is repeated. * be the same as it was before the discontinuity in case the current period is repeated.
*
* <p>This reason also indicates an automatic transition from the content period to an inserted ad
* period or vice versa.
*/ */
int DISCONTINUITY_REASON_PERIOD_TRANSITION = 0; int DISCONTINUITY_REASON_AUTO_TRANSITION = 0;
/** Seek within the current period or to another period. */ /** Seek within the current period or to another period. */
int DISCONTINUITY_REASON_SEEK = 1; int DISCONTINUITY_REASON_SEEK = 1;
/** /**
...@@ -959,10 +1058,12 @@ public interface Player { ...@@ -959,10 +1058,12 @@ public interface Player {
* permitted to be inexact. * permitted to be inexact.
*/ */
int DISCONTINUITY_REASON_SEEK_ADJUSTMENT = 2; int DISCONTINUITY_REASON_SEEK_ADJUSTMENT = 2;
/** Discontinuity to or from an ad within one period in the timeline. */ /** Discontinuity introduced by a skipped period (for instance a skipped ad). */
int DISCONTINUITY_REASON_AD_INSERTION = 3; int DISCONTINUITY_REASON_SKIP = 3;
/** Discontinuity caused by the removal of the current period from the {@link Timeline}. */
int DISCONTINUITY_REASON_REMOVE = 4;
/** Discontinuity introduced internally by the source. */ /** Discontinuity introduced internally by the source. */
int DISCONTINUITY_REASON_INTERNAL = 4; int DISCONTINUITY_REASON_INTERNAL = 5;
/** /**
* Reasons for timeline changes. One of {@link #TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED} or {@link * Reasons for timeline changes. One of {@link #TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED} or {@link
...@@ -1053,7 +1154,10 @@ public interface Player { ...@@ -1053,7 +1154,10 @@ public interface Player {
int EVENT_SHUFFLE_MODE_ENABLED_CHANGED = 10; int EVENT_SHUFFLE_MODE_ENABLED_CHANGED = 10;
/** {@link #getPlayerError()} changed. */ /** {@link #getPlayerError()} changed. */
int EVENT_PLAYER_ERROR = 11; int EVENT_PLAYER_ERROR = 11;
/** A position discontinuity occurred. See {@link EventListener#onPositionDiscontinuity(int)}. */ /**
* A position discontinuity occurred. See {@link
* EventListener#onPositionDiscontinuity(PositionInfo, PositionInfo, int)}.
*/
int EVENT_POSITION_DISCONTINUITY = 12; int EVENT_POSITION_DISCONTINUITY = 12;
/** {@link #getPlaybackParameters()} changed. */ /** {@link #getPlaybackParameters()} changed. */
int EVENT_PLAYBACK_PARAMETERS_CHANGED = 13; int EVENT_PLAYBACK_PARAMETERS_CHANGED = 13;
......
...@@ -50,6 +50,8 @@ import java.util.List; ...@@ -50,6 +50,8 @@ import java.util.List;
* suspended content. * suspended content.
*/ */
public final long requestedContentPositionUs; public final long requestedContentPositionUs;
/** The start position after a reported position discontinuity, in microseconds. */
public final long discontinuityStartPositionUs;
/** The current playback state. One of the {@link Player}.STATE_ constants. */ /** The current playback state. One of the {@link Player}.STATE_ constants. */
@Player.State public final int playbackState; @Player.State public final int playbackState;
/** The current playback error, or null if this is not an error state. */ /** The current playback error, or null if this is not an error state. */
...@@ -104,6 +106,7 @@ import java.util.List; ...@@ -104,6 +106,7 @@ import java.util.List;
Timeline.EMPTY, Timeline.EMPTY,
PLACEHOLDER_MEDIA_PERIOD_ID, PLACEHOLDER_MEDIA_PERIOD_ID,
/* requestedContentPositionUs= */ C.TIME_UNSET, /* requestedContentPositionUs= */ C.TIME_UNSET,
/* discontinuityStartPositionUs= */ 0,
Player.STATE_IDLE, Player.STATE_IDLE,
/* playbackError= */ null, /* playbackError= */ null,
/* isLoading= */ false, /* isLoading= */ false,
...@@ -147,6 +150,7 @@ import java.util.List; ...@@ -147,6 +150,7 @@ import java.util.List;
Timeline timeline, Timeline timeline,
MediaPeriodId periodId, MediaPeriodId periodId,
long requestedContentPositionUs, long requestedContentPositionUs,
long discontinuityStartPositionUs,
@Player.State int playbackState, @Player.State int playbackState,
@Nullable ExoPlaybackException playbackError, @Nullable ExoPlaybackException playbackError,
boolean isLoading, boolean isLoading,
...@@ -165,6 +169,7 @@ import java.util.List; ...@@ -165,6 +169,7 @@ import java.util.List;
this.timeline = timeline; this.timeline = timeline;
this.periodId = periodId; this.periodId = periodId;
this.requestedContentPositionUs = requestedContentPositionUs; this.requestedContentPositionUs = requestedContentPositionUs;
this.discontinuityStartPositionUs = discontinuityStartPositionUs;
this.playbackState = playbackState; this.playbackState = playbackState;
this.playbackError = playbackError; this.playbackError = playbackError;
this.isLoading = isLoading; this.isLoading = isLoading;
...@@ -207,6 +212,7 @@ import java.util.List; ...@@ -207,6 +212,7 @@ import java.util.List;
MediaPeriodId periodId, MediaPeriodId periodId,
long positionUs, long positionUs,
long requestedContentPositionUs, long requestedContentPositionUs,
long discontinuityStartPositionUs,
long totalBufferedDurationUs, long totalBufferedDurationUs,
TrackGroupArray trackGroups, TrackGroupArray trackGroups,
TrackSelectorResult trackSelectorResult, TrackSelectorResult trackSelectorResult,
...@@ -215,6 +221,7 @@ import java.util.List; ...@@ -215,6 +221,7 @@ import java.util.List;
timeline, timeline,
periodId, periodId,
requestedContentPositionUs, requestedContentPositionUs,
discontinuityStartPositionUs,
playbackState, playbackState,
playbackError, playbackError,
isLoading, isLoading,
...@@ -244,6 +251,7 @@ import java.util.List; ...@@ -244,6 +251,7 @@ import java.util.List;
timeline, timeline,
periodId, periodId,
requestedContentPositionUs, requestedContentPositionUs,
discontinuityStartPositionUs,
playbackState, playbackState,
playbackError, playbackError,
isLoading, isLoading,
...@@ -273,6 +281,7 @@ import java.util.List; ...@@ -273,6 +281,7 @@ import java.util.List;
timeline, timeline,
periodId, periodId,
requestedContentPositionUs, requestedContentPositionUs,
discontinuityStartPositionUs,
playbackState, playbackState,
playbackError, playbackError,
isLoading, isLoading,
...@@ -302,6 +311,7 @@ import java.util.List; ...@@ -302,6 +311,7 @@ import java.util.List;
timeline, timeline,
periodId, periodId,
requestedContentPositionUs, requestedContentPositionUs,
discontinuityStartPositionUs,
playbackState, playbackState,
playbackError, playbackError,
isLoading, isLoading,
...@@ -331,6 +341,7 @@ import java.util.List; ...@@ -331,6 +341,7 @@ import java.util.List;
timeline, timeline,
periodId, periodId,
requestedContentPositionUs, requestedContentPositionUs,
discontinuityStartPositionUs,
playbackState, playbackState,
playbackError, playbackError,
isLoading, isLoading,
...@@ -360,6 +371,7 @@ import java.util.List; ...@@ -360,6 +371,7 @@ import java.util.List;
timeline, timeline,
periodId, periodId,
requestedContentPositionUs, requestedContentPositionUs,
discontinuityStartPositionUs,
playbackState, playbackState,
playbackError, playbackError,
isLoading, isLoading,
...@@ -393,6 +405,7 @@ import java.util.List; ...@@ -393,6 +405,7 @@ import java.util.List;
timeline, timeline,
periodId, periodId,
requestedContentPositionUs, requestedContentPositionUs,
discontinuityStartPositionUs,
playbackState, playbackState,
playbackError, playbackError,
isLoading, isLoading,
...@@ -422,6 +435,7 @@ import java.util.List; ...@@ -422,6 +435,7 @@ import java.util.List;
timeline, timeline,
periodId, periodId,
requestedContentPositionUs, requestedContentPositionUs,
discontinuityStartPositionUs,
playbackState, playbackState,
playbackError, playbackError,
isLoading, isLoading,
...@@ -452,6 +466,7 @@ import java.util.List; ...@@ -452,6 +466,7 @@ import java.util.List;
timeline, timeline,
periodId, periodId,
requestedContentPositionUs, requestedContentPositionUs,
discontinuityStartPositionUs,
playbackState, playbackState,
playbackError, playbackError,
isLoading, isLoading,
...@@ -481,6 +496,7 @@ import java.util.List; ...@@ -481,6 +496,7 @@ import java.util.List;
timeline, timeline,
periodId, periodId,
requestedContentPositionUs, requestedContentPositionUs,
discontinuityStartPositionUs,
playbackState, playbackState,
playbackError, playbackError,
isLoading, isLoading,
......
...@@ -706,8 +706,13 @@ public class AnalyticsCollector ...@@ -706,8 +706,13 @@ public class AnalyticsCollector
listener -> listener.onPlayerError(eventTime, error)); listener -> listener.onPlayerError(eventTime, error));
} }
// Calling deprecated callback.
@SuppressWarnings("deprecation")
@Override @Override
public final void onPositionDiscontinuity(@Player.DiscontinuityReason int reason) { public final void onPositionDiscontinuity(
Player.PositionInfo oldPosition,
Player.PositionInfo newPosition,
@Player.DiscontinuityReason int reason) {
if (reason == Player.DISCONTINUITY_REASON_SEEK) { if (reason == Player.DISCONTINUITY_REASON_SEEK) {
isSeeking = false; isSeeking = false;
} }
...@@ -716,7 +721,10 @@ public class AnalyticsCollector ...@@ -716,7 +721,10 @@ public class AnalyticsCollector
sendEvent( sendEvent(
eventTime, eventTime,
AnalyticsListener.EVENT_POSITION_DISCONTINUITY, AnalyticsListener.EVENT_POSITION_DISCONTINUITY,
listener -> listener.onPositionDiscontinuity(eventTime, reason)); listener -> {
listener.onPositionDiscontinuity(eventTime, reason);
listener.onPositionDiscontinuity(eventTime, oldPosition, newPosition, reason);
});
} }
@Override @Override
......
...@@ -236,7 +236,7 @@ public interface AnalyticsListener { ...@@ -236,7 +236,7 @@ public interface AnalyticsListener {
int EVENT_PLAYER_ERROR = Player.EVENT_PLAYER_ERROR; int EVENT_PLAYER_ERROR = Player.EVENT_PLAYER_ERROR;
/** /**
* A position discontinuity occurred. See {@link * A position discontinuity occurred. See {@link
* Player.EventListener#onPositionDiscontinuity(int)}. * Player.EventListener#onPositionDiscontinuity(Player.PositionInfo, Player.PositionInfo, int)}.
*/ */
int EVENT_POSITION_DISCONTINUITY = Player.EVENT_POSITION_DISCONTINUITY; int EVENT_POSITION_DISCONTINUITY = Player.EVENT_POSITION_DISCONTINUITY;
/** {@link Player#getPlaybackParameters()} changed. */ /** {@link Player#getPlaybackParameters()} changed. */
...@@ -532,12 +532,25 @@ public interface AnalyticsListener { ...@@ -532,12 +532,25 @@ public interface AnalyticsListener {
@Player.MediaItemTransitionReason int reason) {} @Player.MediaItemTransitionReason int reason) {}
/** /**
* @deprecated Use {@link #onPositionDiscontinuity(EventTime, Player.PositionInfo,
* Player.PositionInfo, int)} instead.
*/
@Deprecated
default void onPositionDiscontinuity(EventTime eventTime, @DiscontinuityReason int reason) {}
/**
* Called when a position discontinuity occurred. * Called when a position discontinuity occurred.
* *
* @param eventTime The event time. * @param eventTime The event time.
* @param oldPosition The position before the discontinuity.
* @param newPosition The position after the discontinuity.
* @param reason The reason for the position discontinuity. * @param reason The reason for the position discontinuity.
*/ */
default void onPositionDiscontinuity(EventTime eventTime, @DiscontinuityReason int reason) {} default void onPositionDiscontinuity(
EventTime eventTime,
Player.PositionInfo oldPosition,
Player.PositionInfo newPosition,
@DiscontinuityReason int reason) {}
/** /**
* Called when a seek operation started. * Called when a seek operation started.
......
...@@ -191,9 +191,7 @@ public final class DefaultPlaybackSessionManager implements PlaybackSessionManag ...@@ -191,9 +191,7 @@ public final class DefaultPlaybackSessionManager implements PlaybackSessionManag
public synchronized void updateSessionsWithDiscontinuity( public synchronized void updateSessionsWithDiscontinuity(
EventTime eventTime, @DiscontinuityReason int reason) { EventTime eventTime, @DiscontinuityReason int reason) {
Assertions.checkNotNull(listener); Assertions.checkNotNull(listener);
boolean hasAutomaticTransition = boolean hasAutomaticTransition = reason == Player.DISCONTINUITY_REASON_AUTO_TRANSITION;
reason == Player.DISCONTINUITY_REASON_PERIOD_TRANSITION
|| reason == Player.DISCONTINUITY_REASON_AD_INSERTION;
Iterator<SessionDescriptor> iterator = sessions.values().iterator(); Iterator<SessionDescriptor> iterator = sessions.values().iterator();
while (iterator.hasNext()) { while (iterator.hasNext()) {
SessionDescriptor session = iterator.next(); SessionDescriptor session = iterator.next();
......
...@@ -89,7 +89,10 @@ public class DebugTextViewHelper implements Player.EventListener, Runnable { ...@@ -89,7 +89,10 @@ public class DebugTextViewHelper implements Player.EventListener, Runnable {
} }
@Override @Override
public final void onPositionDiscontinuity(@Player.DiscontinuityReason int reason) { public final void onPositionDiscontinuity(
Player.PositionInfo oldPosition,
Player.PositionInfo newPosition,
@Player.DiscontinuityReason int reason) {
updateAndPost(); updateAndPost();
} }
......
...@@ -141,8 +141,50 @@ public class EventLogger implements AnalyticsListener { ...@@ -141,8 +141,50 @@ public class EventLogger implements AnalyticsListener {
} }
@Override @Override
public void onPositionDiscontinuity(EventTime eventTime, @Player.DiscontinuityReason int reason) { public void onPositionDiscontinuity(
logd(eventTime, "positionDiscontinuity", getDiscontinuityReasonString(reason)); EventTime eventTime,
Player.PositionInfo oldPosition,
Player.PositionInfo newPosition,
@Player.DiscontinuityReason int reason) {
StringBuilder builder = new StringBuilder();
builder
.append("reason=")
.append(getDiscontinuityReasonString(reason))
.append(", PositionInfo:old [")
.append("window=")
.append(oldPosition.windowIndex)
.append(", period=")
.append(oldPosition.periodIndex)
.append(", pos=")
.append(oldPosition.positionMs);
if (oldPosition.adGroupIndex != C.INDEX_UNSET) {
builder
.append(", contentPos=")
.append(oldPosition.contentPositionMs)
.append(", adGroup=")
.append(oldPosition.adGroupIndex)
.append(", ad=")
.append(oldPosition.adIndexInAdGroup);
}
builder
.append("], PositionInfo:new [")
.append("window=")
.append(newPosition.windowIndex)
.append(", period=")
.append(newPosition.periodIndex)
.append(", pos=")
.append(newPosition.positionMs);
if (newPosition.adGroupIndex != C.INDEX_UNSET) {
builder
.append(", contentPos=")
.append(newPosition.contentPositionMs)
.append(", adGroup=")
.append(newPosition.adGroupIndex)
.append(", ad=")
.append(newPosition.adIndexInAdGroup);
}
builder.append("]");
logd(eventTime, "positionDiscontinuity", builder.toString());
} }
@Override @Override
...@@ -658,14 +700,16 @@ public class EventLogger implements AnalyticsListener { ...@@ -658,14 +700,16 @@ public class EventLogger implements AnalyticsListener {
private static String getDiscontinuityReasonString(@Player.DiscontinuityReason int reason) { private static String getDiscontinuityReasonString(@Player.DiscontinuityReason int reason) {
switch (reason) { switch (reason) {
case Player.DISCONTINUITY_REASON_PERIOD_TRANSITION: case Player.DISCONTINUITY_REASON_AUTO_TRANSITION:
return "PERIOD_TRANSITION"; return "AUTO_TRANSITION";
case Player.DISCONTINUITY_REASON_SEEK: case Player.DISCONTINUITY_REASON_SEEK:
return "SEEK"; return "SEEK";
case Player.DISCONTINUITY_REASON_SEEK_ADJUSTMENT: case Player.DISCONTINUITY_REASON_SEEK_ADJUSTMENT:
return "SEEK_ADJUSTMENT"; return "SEEK_ADJUSTMENT";
case Player.DISCONTINUITY_REASON_AD_INSERTION: case Player.DISCONTINUITY_REASON_REMOVE:
return "AD_INSERTION"; return "REMOVE";
case Player.DISCONTINUITY_REASON_SKIP:
return "SKIP";
case Player.DISCONTINUITY_REASON_INTERNAL: case Player.DISCONTINUITY_REASON_INTERNAL:
return "INTERNAL"; return "INTERNAL";
default: default:
......
...@@ -430,6 +430,7 @@ public final class MediaPeriodQueueTest { ...@@ -430,6 +430,7 @@ public final class MediaPeriodQueueTest {
mediaPeriodQueue.resolveMediaPeriodIdForAds( mediaPeriodQueue.resolveMediaPeriodIdForAds(
playlistTimeline, firstPeriodUid, /* positionUs= */ 0), playlistTimeline, firstPeriodUid, /* positionUs= */ 0),
/* requestedContentPositionUs= */ C.TIME_UNSET, /* requestedContentPositionUs= */ C.TIME_UNSET,
/* discontinuityStartPositionUs= */ 0,
Player.STATE_READY, Player.STATE_READY,
/* playbackError= */ null, /* playbackError= */ null,
/* isLoading= */ false, /* isLoading= */ false,
......
...@@ -662,6 +662,8 @@ public final class AnalyticsCollectorTest { ...@@ -662,6 +662,8 @@ public final class AnalyticsCollectorTest {
period0Seq0 /* SOURCE_UPDATE */, period0Seq0 /* SOURCE_UPDATE */,
WINDOW_0 /* PLAYLIST_CHANGE */, WINDOW_0 /* PLAYLIST_CHANGE */,
period0Seq1 /* SOURCE_UPDATE */); period0Seq1 /* SOURCE_UPDATE */);
assertThat(listener.getEvents(EVENT_POSITION_DISCONTINUITY))
.containsExactly(WINDOW_0 /* REMOVE */);
assertThat(listener.getEvents(EVENT_IS_LOADING_CHANGED)) assertThat(listener.getEvents(EVENT_IS_LOADING_CHANGED))
.containsExactly(period0Seq0, period0Seq0, period0Seq1, period0Seq1) .containsExactly(period0Seq0, period0Seq0, period0Seq1, period0Seq1)
.inOrder(); .inOrder();
...@@ -937,6 +939,9 @@ public final class AnalyticsCollectorTest { ...@@ -937,6 +939,9 @@ public final class AnalyticsCollectorTest {
period0Seq0 /* SOURCE_UPDATE (second item) */, period0Seq0 /* SOURCE_UPDATE (second item) */,
period0Seq1 /* PLAYLIST_CHANGED (remove) */) period0Seq1 /* PLAYLIST_CHANGED (remove) */)
.inOrder(); .inOrder();
assertThat(listener.getEvents(EVENT_POSITION_DISCONTINUITY))
.containsExactly(period0Seq1 /* REMOVE */)
.inOrder();
assertThat(listener.getEvents(EVENT_IS_LOADING_CHANGED)) assertThat(listener.getEvents(EVENT_IS_LOADING_CHANGED))
.containsExactly(period0Seq0, period0Seq0, period0Seq0, period0Seq0); .containsExactly(period0Seq0, period0Seq0, period0Seq0, period0Seq0);
assertThat(listener.getEvents(EVENT_TRACKS_CHANGED)) assertThat(listener.getEvents(EVENT_TRACKS_CHANGED))
...@@ -1037,9 +1042,11 @@ public final class AnalyticsCollectorTest { ...@@ -1037,9 +1042,11 @@ public final class AnalyticsCollectorTest {
new Player.EventListener() { new Player.EventListener() {
@Override @Override
public void onPositionDiscontinuity( public void onPositionDiscontinuity(
Player.PositionInfo oldPosition,
Player.PositionInfo newPosition,
@Player.DiscontinuityReason int reason) { @Player.DiscontinuityReason int reason) {
if (!player.isPlayingAd() if (!player.isPlayingAd()
&& reason == Player.DISCONTINUITY_REASON_AD_INSERTION) { && reason == Player.DISCONTINUITY_REASON_AUTO_TRANSITION) {
// Finished playing ad. Marked as played. // Finished playing ad. Marked as played.
adPlaybackState.set( adPlaybackState.set(
adPlaybackState adPlaybackState
...@@ -1651,7 +1658,7 @@ public final class AnalyticsCollectorTest { ...@@ -1651,7 +1658,7 @@ public final class AnalyticsCollectorTest {
player.addMediaSource(new FakeMediaSource(new FakeTimeline(), formats)); player.addMediaSource(new FakeMediaSource(new FakeTimeline(), formats));
player.play(); player.play();
TestPlayerRunHelper.runUntilPositionDiscontinuity( TestPlayerRunHelper.runUntilPositionDiscontinuity(
player, Player.DISCONTINUITY_REASON_PERIOD_TRANSITION); player, Player.DISCONTINUITY_REASON_AUTO_TRANSITION);
player.setMediaItem(MediaItem.fromUri("http://this-will-throw-an-exception.mp4")); player.setMediaItem(MediaItem.fromUri("http://this-will-throw-an-exception.mp4"));
TestPlayerRunHelper.runUntilPlaybackState(player, Player.STATE_IDLE); TestPlayerRunHelper.runUntilPlaybackState(player, Player.STATE_IDLE);
ShadowLooper.runMainLooperToNextTask(); ShadowLooper.runMainLooperToNextTask();
...@@ -2085,7 +2092,11 @@ public final class AnalyticsCollectorTest { ...@@ -2085,7 +2092,11 @@ public final class AnalyticsCollectorTest {
} }
@Override @Override
public void onPositionDiscontinuity(EventTime eventTime, int reason) { public void onPositionDiscontinuity(
EventTime eventTime,
Player.PositionInfo oldPosition,
Player.PositionInfo newPosition,
int reason) {
reportedEvents.add(new ReportedEvent(EVENT_POSITION_DISCONTINUITY, eventTime)); reportedEvents.add(new ReportedEvent(EVENT_POSITION_DISCONTINUITY, eventTime));
} }
......
...@@ -461,9 +461,9 @@ public final class DefaultPlaybackSessionManagerTest { ...@@ -461,9 +461,9 @@ public final class DefaultPlaybackSessionManagerTest {
sessionManager.updateSessionsWithTimelineChange(contentEventTime1); sessionManager.updateSessionsWithTimelineChange(contentEventTime1);
sessionManager.updateSessions(adEventTime1); sessionManager.updateSessions(adEventTime1);
sessionManager.updateSessionsWithDiscontinuity( sessionManager.updateSessionsWithDiscontinuity(
adEventTime1, Player.DISCONTINUITY_REASON_AD_INSERTION); adEventTime1, Player.DISCONTINUITY_REASON_AUTO_TRANSITION);
sessionManager.updateSessionsWithDiscontinuity( sessionManager.updateSessionsWithDiscontinuity(
contentEventTime2, Player.DISCONTINUITY_REASON_AD_INSERTION); contentEventTime2, Player.DISCONTINUITY_REASON_AUTO_TRANSITION);
String adSessionId2 = String adSessionId2 =
sessionManager.getSessionForMediaPeriodId(adTimeline, adEventTime2.mediaPeriodId); sessionManager.getSessionForMediaPeriodId(adTimeline, adEventTime2.mediaPeriodId);
...@@ -751,7 +751,7 @@ public final class DefaultPlaybackSessionManagerTest { ...@@ -751,7 +751,7 @@ public final class DefaultPlaybackSessionManagerTest {
sessionManager.updateSessions(eventTime2); sessionManager.updateSessions(eventTime2);
sessionManager.updateSessionsWithDiscontinuity( sessionManager.updateSessionsWithDiscontinuity(
eventTime2, Player.DISCONTINUITY_REASON_PERIOD_TRANSITION); eventTime2, Player.DISCONTINUITY_REASON_AUTO_TRANSITION);
verify(mockListener).onSessionCreated(eq(eventTime1), anyString()); verify(mockListener).onSessionCreated(eq(eventTime1), anyString());
verify(mockListener).onSessionActive(eq(eventTime1), anyString()); verify(mockListener).onSessionActive(eq(eventTime1), anyString());
...@@ -781,7 +781,7 @@ public final class DefaultPlaybackSessionManagerTest { ...@@ -781,7 +781,7 @@ public final class DefaultPlaybackSessionManagerTest {
sessionManager.getSessionForMediaPeriodId(timeline, eventTime2.mediaPeriodId); sessionManager.getSessionForMediaPeriodId(timeline, eventTime2.mediaPeriodId);
sessionManager.updateSessionsWithDiscontinuity( sessionManager.updateSessionsWithDiscontinuity(
eventTime2, Player.DISCONTINUITY_REASON_PERIOD_TRANSITION); eventTime2, Player.DISCONTINUITY_REASON_AUTO_TRANSITION);
verify(mockListener).onSessionCreated(eventTime1, sessionId1); verify(mockListener).onSessionCreated(eventTime1, sessionId1);
verify(mockListener).onSessionActive(eventTime1, sessionId1); verify(mockListener).onSessionActive(eventTime1, sessionId1);
...@@ -960,7 +960,7 @@ public final class DefaultPlaybackSessionManagerTest { ...@@ -960,7 +960,7 @@ public final class DefaultPlaybackSessionManagerTest {
adTimeline, contentEventTimeDuringPreroll.mediaPeriodId); adTimeline, contentEventTimeDuringPreroll.mediaPeriodId);
sessionManager.updateSessionsWithDiscontinuity( sessionManager.updateSessionsWithDiscontinuity(
contentEventTimeBetweenAds, Player.DISCONTINUITY_REASON_AD_INSERTION); contentEventTimeBetweenAds, Player.DISCONTINUITY_REASON_AUTO_TRANSITION);
InOrder inOrder = inOrder(mockListener); InOrder inOrder = inOrder(mockListener);
inOrder.verify(mockListener).onSessionCreated(contentEventTimeDuringPreroll, contentSessionId); inOrder.verify(mockListener).onSessionCreated(contentEventTimeDuringPreroll, contentSessionId);
...@@ -1025,7 +1025,7 @@ public final class DefaultPlaybackSessionManagerTest { ...@@ -1025,7 +1025,7 @@ public final class DefaultPlaybackSessionManagerTest {
sessionManager.updateSessions(adEventTime2); sessionManager.updateSessions(adEventTime2);
sessionManager.updateSessionsWithDiscontinuity( sessionManager.updateSessionsWithDiscontinuity(
adEventTime1, Player.DISCONTINUITY_REASON_AD_INSERTION); adEventTime1, Player.DISCONTINUITY_REASON_AUTO_TRANSITION);
verify(mockListener, never()).onSessionFinished(any(), anyString(), anyBoolean()); verify(mockListener, never()).onSessionFinished(any(), anyString(), anyBoolean());
} }
...@@ -1083,7 +1083,7 @@ public final class DefaultPlaybackSessionManagerTest { ...@@ -1083,7 +1083,7 @@ public final class DefaultPlaybackSessionManagerTest {
sessionManager.getSessionForMediaPeriodId(adTimeline, adEventTime2.mediaPeriodId); sessionManager.getSessionForMediaPeriodId(adTimeline, adEventTime2.mediaPeriodId);
sessionManager.updateSessionsWithDiscontinuity( sessionManager.updateSessionsWithDiscontinuity(
adEventTime1, Player.DISCONTINUITY_REASON_AD_INSERTION); adEventTime1, Player.DISCONTINUITY_REASON_AUTO_TRANSITION);
sessionManager.updateSessionsWithDiscontinuity(adEventTime2, Player.DISCONTINUITY_REASON_SEEK); sessionManager.updateSessionsWithDiscontinuity(adEventTime2, Player.DISCONTINUITY_REASON_SEEK);
verify(mockListener).onSessionCreated(eq(contentEventTime), anyString()); verify(mockListener).onSessionCreated(eq(contentEventTime), anyString());
......
...@@ -1625,7 +1625,10 @@ public class PlayerView extends FrameLayout implements AdsLoader.AdViewProvider ...@@ -1625,7 +1625,10 @@ public class PlayerView extends FrameLayout implements AdsLoader.AdViewProvider
} }
@Override @Override
public void onPositionDiscontinuity(@DiscontinuityReason int reason) { public void onPositionDiscontinuity(
Player.PositionInfo oldPosition,
Player.PositionInfo newPosition,
@DiscontinuityReason int reason) {
if (isPlayingAd() && controllerHideDuringAds) { if (isPlayingAd() && controllerHideDuringAds) {
hideController(); hideController();
} }
......
...@@ -1647,7 +1647,10 @@ public class StyledPlayerView extends FrameLayout implements AdsLoader.AdViewPro ...@@ -1647,7 +1647,10 @@ public class StyledPlayerView extends FrameLayout implements AdsLoader.AdViewPro
} }
@Override @Override
public void onPositionDiscontinuity(@DiscontinuityReason int reason) { public void onPositionDiscontinuity(
Player.PositionInfo oldPosition,
Player.PositionInfo newPosition,
@DiscontinuityReason int reason) {
if (isPlayingAd() && controllerHideDuringAds) { if (isPlayingAd() && controllerHideDuringAds) {
hideController(); hideController();
} }
......
...@@ -158,8 +158,8 @@ public class TestPlayerRunHelper { ...@@ -158,8 +158,8 @@ public class TestPlayerRunHelper {
/** /**
* Runs tasks of the main {@link Looper} until a {@link * Runs tasks of the main {@link Looper} until a {@link
* Player.EventListener#onPositionDiscontinuity} callback with the specified {@link * Player.EventListener#onPositionDiscontinuity(Player.PositionInfo, Player.PositionInfo, int)}
* Player.DiscontinuityReason} occurred. * callback with the specified {@link Player.DiscontinuityReason} occurred.
* *
* @param player The {@link Player}. * @param player The {@link Player}.
* @param expectedReason The expected {@link Player.DiscontinuityReason}. * @param expectedReason The expected {@link Player.DiscontinuityReason}.
...@@ -173,7 +173,8 @@ public class TestPlayerRunHelper { ...@@ -173,7 +173,8 @@ public class TestPlayerRunHelper {
Player.EventListener listener = Player.EventListener listener =
new Player.EventListener() { new Player.EventListener() {
@Override @Override
public void onPositionDiscontinuity(int reason) { public void onPositionDiscontinuity(
Player.PositionInfo oldPosition, Player.PositionInfo newPosition, int reason) {
if (reason == expectedReason) { if (reason == expectedReason) {
receivedCallback.set(true); receivedCallback.set(true);
player.removeListener(this); player.removeListener(this);
......
...@@ -803,7 +803,10 @@ public abstract class Action { ...@@ -803,7 +803,10 @@ public abstract class Action {
} }
} }
/** Waits for {@link Player.EventListener#onPositionDiscontinuity(int)}. */ /**
* Waits for {@link Player.EventListener#onPositionDiscontinuity(Player.PositionInfo,
* Player.PositionInfo, int)}.
*/
public static final class WaitForPositionDiscontinuity extends Action { public static final class WaitForPositionDiscontinuity extends Action {
/** @param tag A tag to use for logging. */ /** @param tag A tag to use for logging. */
...@@ -824,7 +827,10 @@ public abstract class Action { ...@@ -824,7 +827,10 @@ public abstract class Action {
player.addListener( player.addListener(
new Player.EventListener() { new Player.EventListener() {
@Override @Override
public void onPositionDiscontinuity(@Player.DiscontinuityReason int reason) { public void onPositionDiscontinuity(
Player.PositionInfo oldPosition,
Player.PositionInfo newPosition,
@Player.DiscontinuityReason int reason) {
player.removeListener(this); player.removeListener(this);
nextAction.schedule(player, trackSelector, surface, handler); nextAction.schedule(player, trackSelector, surface, handler);
} }
......
...@@ -575,7 +575,8 @@ public final class ExoPlayerTestRunner implements Player.EventListener, ActionSc ...@@ -575,7 +575,8 @@ public final class ExoPlayerTestRunner implements Player.EventListener, ActionSc
} }
/** /**
* Asserts that {@link Player.EventListener#onPositionDiscontinuity(int)} was not called. * Asserts that {@link Player.EventListener#onPositionDiscontinuity(Player.PositionInfo,
* Player.PositionInfo, int)} was not called.
*/ */
public void assertNoPositionDiscontinuities() { public void assertNoPositionDiscontinuities() {
assertThat(discontinuityReasons).isEmpty(); assertThat(discontinuityReasons).isEmpty();
...@@ -583,7 +584,8 @@ public final class ExoPlayerTestRunner implements Player.EventListener, ActionSc ...@@ -583,7 +584,8 @@ public final class ExoPlayerTestRunner implements Player.EventListener, ActionSc
/** /**
* Asserts that the discontinuity reasons reported by {@link * Asserts that the discontinuity reasons reported by {@link
* Player.EventListener#onPositionDiscontinuity(int)} are equal to the provided values. * Player.EventListener#onPositionDiscontinuity(Player.PositionInfo, Player.PositionInfo, int)}
* are equal to the provided values.
* *
* @param discontinuityReasons The expected discontinuity reasons. * @param discontinuityReasons The expected discontinuity reasons.
*/ */
...@@ -676,10 +678,15 @@ public final class ExoPlayerTestRunner implements Player.EventListener, ActionSc ...@@ -676,10 +678,15 @@ public final class ExoPlayerTestRunner implements Player.EventListener, ActionSc
} }
@Override @Override
public void onPositionDiscontinuity(@Player.DiscontinuityReason int reason) { public void onPositionDiscontinuity(
Player.PositionInfo oldPosition,
Player.PositionInfo newPosition,
@Player.DiscontinuityReason int reason) {
discontinuityReasons.add(reason); discontinuityReasons.add(reason);
int currentIndex = player.getCurrentPeriodIndex(); int currentIndex = player.getCurrentPeriodIndex();
if (reason == Player.DISCONTINUITY_REASON_PERIOD_TRANSITION if ((reason == Player.DISCONTINUITY_REASON_AUTO_TRANSITION
&& oldPosition.adGroupIndex != C.INDEX_UNSET
&& newPosition.adGroupIndex != C.INDEX_UNSET)
|| periodIndices.isEmpty() || periodIndices.isEmpty()
|| periodIndices.get(periodIndices.size() - 1) != currentIndex) { || periodIndices.get(periodIndices.size() - 1) != currentIndex) {
// Ignore seek or internal discontinuities within a period. // Ignore seek or internal discontinuities within a period.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment