Skip to content
Toggle navigation
P
Projects
G
Groups
S
Snippets
Help
SDK
/
exoplayer
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
0
Merge Requests
0
Pipelines
Wiki
Snippets
Settings
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Commit
dc4148d5
authored
Mar 24, 2021
by
bachinger
Committed by
Oliver Woodman
Mar 24, 2021
Browse files
Options
_('Browse Files')
Download
Email Patches
Plain Diff
Add positions and new reasons to onPositionDiscontinuity
PiperOrigin-RevId: 364861539
parent
f19ab4aa
Hide whitespace changes
Inline
Side-by-side
Showing
26 changed files
with
1586 additions
and
192 deletions
RELEASENOTES.md
extensions/cast/src/main/java/com/google/android/exoplayer2/ext/cast/CastPlayer.java
extensions/ima/src/main/java/com/google/android/exoplayer2/ext/ima/AdTagLoader.java
extensions/ima/src/main/java/com/google/android/exoplayer2/ext/ima/ImaAdsLoader.java
extensions/ima/src/test/java/com/google/android/exoplayer2/ext/ima/FakePlayer.java
extensions/ima/src/test/java/com/google/android/exoplayer2/ext/ima/ImaAdsLoaderTest.java
extensions/leanback/src/main/java/com/google/android/exoplayer2/ext/leanback/LeanbackPlayerAdapter.java
extensions/media2/src/main/java/com/google/android/exoplayer2/ext/media2/PlayerWrapper.java
library/common/src/main/java/com/google/android/exoplayer2/Player.java
library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java
library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java
library/core/src/main/java/com/google/android/exoplayer2/PlaybackInfo.java
library/core/src/main/java/com/google/android/exoplayer2/analytics/AnalyticsCollector.java
library/core/src/main/java/com/google/android/exoplayer2/analytics/AnalyticsListener.java
library/core/src/main/java/com/google/android/exoplayer2/analytics/DefaultPlaybackSessionManager.java
library/core/src/main/java/com/google/android/exoplayer2/util/DebugTextViewHelper.java
library/core/src/main/java/com/google/android/exoplayer2/util/EventLogger.java
library/core/src/test/java/com/google/android/exoplayer2/ExoPlayerTest.java
library/core/src/test/java/com/google/android/exoplayer2/MediaPeriodQueueTest.java
library/core/src/test/java/com/google/android/exoplayer2/analytics/AnalyticsCollectorTest.java
library/core/src/test/java/com/google/android/exoplayer2/analytics/DefaultPlaybackSessionManagerTest.java
library/ui/src/main/java/com/google/android/exoplayer2/ui/PlayerView.java
library/ui/src/main/java/com/google/android/exoplayer2/ui/StyledPlayerView.java
robolectricutils/src/main/java/com/google/android/exoplayer2/robolectric/TestPlayerRunHelper.java
testutils/src/main/java/com/google/android/exoplayer2/testutil/Action.java
testutils/src/main/java/com/google/android/exoplayer2/testutil/ExoPlayerTestRunner.java
RELEASENOTES.md
View file @
dc4148d5
...
...
@@ -2,6 +2,17 @@
### 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:
*
Add builder for
`PlayerNotificationManager`
.
*
Add group setting to
`PlayerNotificationManager`
.
...
...
extensions/cast/src/main/java/com/google/android/exoplayer2/ext/cast/CastPlayer.java
View file @
dc4148d5
...
...
@@ -460,6 +460,7 @@ public final class CastPlayer extends BasePlayer {
pendingSeekCount
++;
pendingSeekWindowIndex
=
windowIndex
;
pendingSeekPositionMs
=
positionMs
;
// TODO(b/181262841): call new onPositionDiscontinuity callback
listeners
.
queueEvent
(
Player
.
EVENT_POSITION_DISCONTINUITY
,
listener
->
listener
.
onPositionDiscontinuity
(
DISCONTINUITY_REASON_SEEK
));
...
...
@@ -630,6 +631,8 @@ public final class CastPlayer extends BasePlayer {
// Internal methods.
// Call deprecated callbacks.
@SuppressWarnings
(
"deprecation"
)
private
void
updateInternalStateAndNotifyIfChanged
()
{
if
(
remoteMediaClient
==
null
)
{
// 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 {
int
currentWindowIndex
=
fetchCurrentWindowIndex
(
remoteMediaClient
,
currentTimeline
);
if
(
this
.
currentWindowIndex
!=
currentWindowIndex
&&
pendingSeekCount
==
0
)
{
this
.
currentWindowIndex
=
currentWindowIndex
;
// TODO(b/181262841): call new onPositionDiscontinuity callback
listeners
.
queueEvent
(
Player
.
EVENT_POSITION_DISCONTINUITY
,
listener
->
listener
.
onPositionDiscontinuity
(
DISCONTINUITY_REASON_
PERIOD
_TRANSITION
));
listener
->
listener
.
onPositionDiscontinuity
(
DISCONTINUITY_REASON_
AUTO
_TRANSITION
));
listeners
.
queueEvent
(
Player
.
EVENT_MEDIA_ITEM_TRANSITION
,
listener
->
...
...
extensions/ima/src/main/java/com/google/android/exoplayer2/ext/ima/AdTagLoader.java
View file @
dc4148d5
...
...
@@ -468,7 +468,10 @@ import java.util.Map;
}
@Override
public
void
onPositionDiscontinuity
(
@Player
.
DiscontinuityReason
int
reason
)
{
public
void
onPositionDiscontinuity
(
Player
.
PositionInfo
oldPosition
,
Player
.
PositionInfo
newPosition
,
@Player
.
DiscontinuityReason
int
reason
)
{
handleTimelineOrPositionChanged
();
}
...
...
extensions/ima/src/main/java/com/google/android/exoplayer2/ext/ima/ImaAdsLoader.java
View file @
dc4148d5
...
...
@@ -613,7 +613,10 @@ public final class ImaAdsLoader implements Player.EventListener, AdsLoader {
}
@Override
public
void
onPositionDiscontinuity
(
@Player
.
DiscontinuityReason
int
reason
)
{
public
void
onPositionDiscontinuity
(
Player
.
PositionInfo
oldPosition
,
Player
.
PositionInfo
newPosition
,
@Player
.
DiscontinuityReason
int
reason
)
{
maybeUpdateCurrentAdTagLoader
();
maybePreloadNextPeriodAds
();
}
...
...
extensions/ima/src/test/java/com/google/android/exoplayer2/ext/ima/FakePlayer.java
View file @
dc4148d5
...
...
@@ -29,6 +29,8 @@ import com.google.android.exoplayer2.util.ListenerSet;
private
final
ListenerSet
<
EventListener
>
listeners
;
private
final
Timeline
.
Period
period
;
private
final
Object
windowUid
=
new
Object
();
private
final
Object
periodUid
=
new
Object
();
private
Timeline
timeline
;
@Player
.
State
private
int
state
;
...
...
@@ -65,6 +67,16 @@ import com.google.android.exoplayer2.util.ListenerSet;
*/
public
void
setPlayingContentPosition
(
int
periodIndex
,
long
positionMs
)
{
boolean
notify
=
isPlayingAd
;
PositionInfo
oldPosition
=
new
PositionInfo
(
windowUid
,
/* windowIndex= */
0
,
periodUid
,
/* periodIndex= */
0
,
this
.
positionMs
,
this
.
contentPositionMs
,
this
.
adGroupIndex
,
this
.
adIndexInAdGroup
);
isPlayingAd
=
false
;
adGroupIndex
=
C
.
INDEX_UNSET
;
adIndexInAdGroup
=
C
.
INDEX_UNSET
;
...
...
@@ -72,9 +84,21 @@ import com.google.android.exoplayer2.util.ListenerSet;
this
.
positionMs
=
positionMs
;
contentPositionMs
=
positionMs
;
if
(
notify
)
{
PositionInfo
newPosition
=
new
PositionInfo
(
windowUid
,
/* windowIndex= */
0
,
periodUid
,
/* periodIndex= */
0
,
positionMs
,
this
.
contentPositionMs
,
this
.
adGroupIndex
,
this
.
adIndexInAdGroup
);
listeners
.
sendEvent
(
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;
long
positionMs
,
long
contentPositionMs
)
{
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
;
this
.
periodIndex
=
periodIndex
;
this
.
adGroupIndex
=
adGroupIndex
;
...
...
@@ -97,9 +131,21 @@ import com.google.android.exoplayer2.util.ListenerSet;
this
.
positionMs
=
positionMs
;
this
.
contentPositionMs
=
contentPositionMs
;
if
(
notify
)
{
PositionInfo
newPosition
=
new
PositionInfo
(
windowUid
,
/* windowIndex= */
0
,
periodUid
,
/* periodIndex= */
0
,
positionMs
,
contentPositionMs
,
adGroupIndex
,
adIndexInAdGroup
);
listeners
.
sendEvent
(
EVENT_POSITION_DISCONTINUITY
,
listener
->
listener
.
onPositionDiscontinuity
(
DISCONTINUITY_REASON_AD_INSERTION
));
listener
->
listener
.
onPositionDiscontinuity
(
oldPosition
,
newPosition
,
DISCONTINUITY_REASON_AUTO_TRANSITION
));
}
}
...
...
extensions/ima/src/test/java/com/google/android/exoplayer2/ext/ima/ImaAdsLoaderTest.java
View file @
dc4148d5
...
...
@@ -281,7 +281,26 @@ public final class ImaAdsLoaderTest {
videoAdPlayer
.
pauseAd
(
TEST_AD_MEDIA_INFO
);
videoAdPlayer
.
stopAd
(
TEST_AD_MEDIA_INFO
);
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
));
imaAdsLoader
.
handlePrepareError
(
adsMediaSource
,
/* adGroupIndex= */
0
,
/* adIndexInAdGroup= */
0
,
new
IOException
());
...
...
extensions/leanback/src/main/java/com/google/android/exoplayer2/ext/leanback/LeanbackPlayerAdapter.java
View file @
dc4148d5
...
...
@@ -306,7 +306,10 @@ public final class LeanbackPlayerAdapter extends PlayerAdapter implements Runnab
}
@Override
public
void
onPositionDiscontinuity
(
@DiscontinuityReason
int
reason
)
{
public
void
onPositionDiscontinuity
(
Player
.
PositionInfo
oldPosition
,
Player
.
PositionInfo
newPosition
,
@DiscontinuityReason
int
reason
)
{
Callback
callback
=
getCallback
();
callback
.
onCurrentPositionChanged
(
LeanbackPlayerAdapter
.
this
);
callback
.
onBufferedPositionChanged
(
LeanbackPlayerAdapter
.
this
);
...
...
extensions/media2/src/main/java/com/google/android/exoplayer2/ext/media2/PlayerWrapper.java
View file @
dc4148d5
...
...
@@ -437,7 +437,7 @@ import java.util.List;
case
Player
.
STATE_READY
:
if
(!
prepared
)
{
prepared
=
true
;
handlePositionDiscontinuity
(
Player
.
DISCONTINUITY_REASON_
PERIOD
_TRANSITION
);
handlePositionDiscontinuity
(
Player
.
DISCONTINUITY_REASON_
AUTO
_TRANSITION
);
listener
.
onPrepared
(
Assertions
.
checkNotNull
(
getCurrentMediaItem
()),
player
.
getBufferedPercentage
());
}
...
...
@@ -517,9 +517,11 @@ import java.util.List;
int
currentWindowIndex
=
getCurrentMediaItemIndex
();
if
(
this
.
currentWindowIndex
!=
currentWindowIndex
)
{
this
.
currentWindowIndex
=
currentWindowIndex
;
androidx
.
media2
.
common
.
MediaItem
currentMediaItem
=
Assertions
.
checkNotNull
(
getCurrentMediaItem
());
listener
.
onCurrentMediaItemChanged
(
currentMediaItem
);
if
(
currentWindowIndex
!=
C
.
INDEX_UNSET
)
{
androidx
.
media2
.
common
.
MediaItem
currentMediaItem
=
Assertions
.
checkNotNull
(
getCurrentMediaItem
());
listener
.
onCurrentMediaItemChanged
(
currentMediaItem
);
}
}
else
{
listener
.
onSeekCompleted
();
}
...
...
@@ -597,7 +599,10 @@ import java.util.List;
}
@Override
public
void
onPositionDiscontinuity
(
@Player
.
DiscontinuityReason
int
reason
)
{
public
void
onPositionDiscontinuity
(
Player
.
PositionInfo
oldPosition
,
Player
.
PositionInfo
newPosition
,
@Player
.
DiscontinuityReason
int
reason
)
{
handlePositionDiscontinuity
(
reason
);
}
...
...
library/common/src/main/java/com/google/android/exoplayer2/Player.java
View file @
dc4148d5
...
...
@@ -15,7 +15,6 @@
*/
package
com
.
google
.
android
.
exoplayer2
;
import
android.content.Context
;
import
android.os.Looper
;
import
android.view.Surface
;
...
...
@@ -40,6 +39,7 @@ import com.google.android.exoplayer2.util.Util;
import
com.google.android.exoplayer2.video.VideoFrameMetadataListener
;
import
com.google.android.exoplayer2.video.VideoListener
;
import
com.google.android.exoplayer2.video.spherical.CameraMotionListener
;
import
com.google.common.base.Objects
;
import
java.lang.annotation.Documented
;
import
java.lang.annotation.Retention
;
import
java.lang.annotation.RetentionPolicy
;
...
...
@@ -396,10 +396,9 @@ public interface Player {
/**
* Called when the timeline has been refreshed.
*
* <p>Note that if the timeline has changed then a position discontinuity may also have
* occurred. For example, the current period index may have changed as a result of periods being
* added or removed from the timeline. This will <em>not</em> be reported via a separate call to
* {@link #onPositionDiscontinuity(int)}.
* <p>Note that the current window or period index may change as a result of a timeline change.
* If playback can't continue smoothly because of this timeline change, a separate {@link
* #onPositionDiscontinuity(PositionInfo, PositionInfo, int)} callback will be triggered.
*
* <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.
...
...
@@ -576,21 +575,27 @@ public interface Player {
default
void
onPlayerError
(
ExoPlaybackException
error
)
{}
/**
* Called when a position discontinuity occurs without a change to the timeline. A position
* 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
* jumps within the period currently being played (as a result of a seek being performed, or
* when the source introduces a discontinuity internally).
* @deprecated Use {@link #onPositionDiscontinuity(PositionInfo, PositionInfo, int)} instead.
*/
@Deprecated
default
void
onPositionDiscontinuity
(
@DiscontinuityReason
int
reason
)
{}
/**
* Called when a position discontinuity occurs.
*
* <p>When a position discontinuity occurs as a result of a change to the timeline this method
* is <em>not</em> called. {@link #onTimelineChanged(Timeline, int)} is called in this case.
* <p>A position discontinuity occurs when the playing period changes, the playback position
* 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
* 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.
*/
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
...
...
@@ -607,7 +612,8 @@ public interface Player {
/**
* @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
default
void
onSeekProcessed
()
{}
...
...
@@ -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}.
*
...
...
@@ -933,25 +1027,30 @@ public interface Player {
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
* #DISCONTINUITY_REASON_AD_INSERTION} or {@link #DISCONTINUITY_REASON_INTERNAL}.
* #DISCONTINUITY_REASON_SKIP}, {@link #DISCONTINUITY_REASON_REMOVE} or {@link
* #DISCONTINUITY_REASON_INTERNAL}.
*/
@Documented
@Retention
(
RetentionPolicy
.
SOURCE
)
@IntDef
({
DISCONTINUITY_REASON_
PERIOD
_TRANSITION
,
DISCONTINUITY_REASON_
AUTO
_TRANSITION
,
DISCONTINUITY_REASON_SEEK
,
DISCONTINUITY_REASON_SEEK_ADJUSTMENT
,
DISCONTINUITY_REASON_AD_INSERTION
,
DISCONTINUITY_REASON_SKIP
,
DISCONTINUITY_REASON_REMOVE
,
DISCONTINUITY_REASON_INTERNAL
})
@interface
DiscontinuityReason
{}
/**
* 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.
*
* <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. */
int
DISCONTINUITY_REASON_SEEK
=
1
;
/**
...
...
@@ -959,10 +1058,12 @@ public interface Player {
* permitted to be inexact.
*/
int
DISCONTINUITY_REASON_SEEK_ADJUSTMENT
=
2
;
/** Discontinuity to or from an ad within one period in the timeline. */
int
DISCONTINUITY_REASON_AD_INSERTION
=
3
;
/** Discontinuity introduced by a skipped period (for instance a skipped ad). */
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. */
int
DISCONTINUITY_REASON_INTERNAL
=
4
;
int
DISCONTINUITY_REASON_INTERNAL
=
5
;
/**
* Reasons for timeline changes. One of {@link #TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED} or {@link
...
...
@@ -1053,7 +1154,10 @@ public interface Player {
int
EVENT_SHUFFLE_MODE_ENABLED_CHANGED
=
10
;
/** {@link #getPlayerError()} changed. */
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
;
/** {@link #getPlaybackParameters()} changed. */
int
EVENT_PLAYBACK_PARAMETERS_CHANGED
=
13
;
...
...
library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java
View file @
dc4148d5
...
...
@@ -87,8 +87,8 @@ import java.util.concurrent.CopyOnWriteArraySet;
@RepeatMode
private
int
repeatMode
;
private
boolean
shuffleModeEnabled
;
private
int
pendingOperationAcks
;
private
boolean
hasPendingDiscontinuity
;
@DiscontinuityReason
private
int
pendingDiscontinuityReason
;
private
boolean
pendingDiscontinuity
;
@PlayWhenReadyChangeReason
private
int
pendingPlayWhenReadyChangeReason
;
private
boolean
foregroundMode
;
private
SeekParameters
seekParameters
;
...
...
@@ -367,11 +367,13 @@ import java.util.concurrent.CopyOnWriteArraySet;
internalPlayer
.
prepare
();
updatePlaybackInfo
(
playbackInfo
,
/* positionDiscontinuity= */
false
,
/* ignored */
DISCONTINUITY_REASON_INTERNAL
,
/* ignored */
TIMELINE_CHANGE_REASON_SOURCE_UPDATE
,
/* ignored */
PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST
,
/* seekProcessed= */
false
);
/* seekProcessed= */
false
,
/* positionDiscontinuity= */
false
,
/* ignored */
DISCONTINUITY_REASON_INTERNAL
,
/* ignored */
C
.
TIME_UNSET
,
/* ignored */
C
.
INDEX_UNSET
);
}
/**
...
...
@@ -479,24 +481,30 @@ import java.util.concurrent.CopyOnWriteArraySet;
internalPlayer
.
addMediaSources
(
index
,
holders
,
shuffleOrder
);
updatePlaybackInfo
(
newPlaybackInfo
,
/* positionDiscontinuity= */
false
,
/* ignored */
DISCONTINUITY_REASON_INTERNAL
,
/* timelineChangeReason= */
TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED
,
/* ignored */
PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST
,
/* seekProcessed= */
false
);
/* seekProcessed= */
false
,
/* positionDiscontinuity= */
false
,
/* ignored */
DISCONTINUITY_REASON_INTERNAL
,
/* ignored */
C
.
TIME_UNSET
,
/* ignored */
C
.
INDEX_UNSET
);
}
@Override
public
void
removeMediaItems
(
int
fromIndex
,
int
toIndex
)
{
toIndex
=
min
(
toIndex
,
mediaSourceHolderSnapshots
.
size
());
PlaybackInfo
playbackInfo
=
removeMediaItemsInternal
(
fromIndex
,
toIndex
);
PlaybackInfo
newPlaybackInfo
=
removeMediaItemsInternal
(
fromIndex
,
toIndex
);
boolean
positionDiscontinuity
=
!
newPlaybackInfo
.
periodId
.
periodUid
.
equals
(
playbackInfo
.
periodId
.
periodUid
);
updatePlaybackInfo
(
playbackInfo
,
/* positionDiscontinuity= */
false
,
/* ignored */
Player
.
DISCONTINUITY_REASON_INTERNAL
,
newPlaybackInfo
,
/* timelineChangeReason= */
TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED
,
/* ignored */
PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST
,
/* seekProcessed= */
false
);
/* seekProcessed= */
false
,
positionDiscontinuity
,
Player
.
DISCONTINUITY_REASON_REMOVE
,
/* discontinuityWindowStartPositionUs= */
getCurrentPositionUsInternal
(
newPlaybackInfo
),
/* ignored */
C
.
INDEX_UNSET
);
}
@Override
...
...
@@ -519,11 +527,13 @@ import java.util.concurrent.CopyOnWriteArraySet;
internalPlayer
.
moveMediaSources
(
fromIndex
,
toIndex
,
newFromIndex
,
shuffleOrder
);
updatePlaybackInfo
(
newPlaybackInfo
,
/* positionDiscontinuity= */
false
,
/* ignored */
DISCONTINUITY_REASON_INTERNAL
,
/* timelineChangeReason= */
TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED
,
/* ignored */
PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST
,
/* seekProcessed= */
false
);
/* seekProcessed= */
false
,
/* positionDiscontinuity= */
false
,
/* ignored */
DISCONTINUITY_REASON_INTERNAL
,
/* ignored */
C
.
TIME_UNSET
,
/* ignored */
C
.
INDEX_UNSET
);
}
@Override
...
...
@@ -540,11 +550,13 @@ import java.util.concurrent.CopyOnWriteArraySet;
internalPlayer
.
setShuffleOrder
(
shuffleOrder
);
updatePlaybackInfo
(
newPlaybackInfo
,
/* positionDiscontinuity= */
false
,
/* ignored */
DISCONTINUITY_REASON_INTERNAL
,
/* timelineChangeReason= */
TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED
,
/* ignored */
PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST
,
/* seekProcessed= */
false
);
/* seekProcessed= */
false
,
/* positionDiscontinuity= */
false
,
/* ignored */
DISCONTINUITY_REASON_INTERNAL
,
/* ignored */
C
.
TIME_UNSET
,
/* ignored */
C
.
INDEX_UNSET
);
}
@Override
...
...
@@ -583,11 +595,13 @@ import java.util.concurrent.CopyOnWriteArraySet;
internalPlayer
.
setPlayWhenReady
(
playWhenReady
,
playbackSuppressionReason
);
updatePlaybackInfo
(
playbackInfo
,
/* positionDiscontinuity= */
false
,
/* ignored */
DISCONTINUITY_REASON_INTERNAL
,
/* ignored */
TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED
,
playWhenReadyChangeReason
,
/* seekProcessed= */
false
);
/* seekProcessed= */
false
,
/* positionDiscontinuity= */
false
,
/* ignored */
DISCONTINUITY_REASON_INTERNAL
,
/* ignored */
C
.
TIME_UNSET
,
/* ignored */
C
.
INDEX_UNSET
);
}
@Override
...
...
@@ -656,7 +670,8 @@ import java.util.concurrent.CopyOnWriteArraySet;
@Player
.
State
int
newPlaybackState
=
getPlaybackState
()
==
Player
.
STATE_IDLE
?
Player
.
STATE_IDLE
:
Player
.
STATE_BUFFERING
;
PlaybackInfo
newPlaybackInfo
=
this
.
playbackInfo
.
copyWithPlaybackState
(
newPlaybackState
);
int
oldMaskingWindowIndex
=
getCurrentWindowIndex
();
PlaybackInfo
newPlaybackInfo
=
playbackInfo
.
copyWithPlaybackState
(
newPlaybackState
);
newPlaybackInfo
=
maskTimelineAndPosition
(
newPlaybackInfo
,
...
...
@@ -665,11 +680,13 @@ import java.util.concurrent.CopyOnWriteArraySet;
internalPlayer
.
seekTo
(
timeline
,
windowIndex
,
C
.
msToUs
(
positionMs
));
updatePlaybackInfo
(
newPlaybackInfo
,
/* positionDiscontinuity= */
true
,
/* positionDiscontinuityReason= */
DISCONTINUITY_REASON_SEEK
,
/* ignored */
TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED
,
/* ignored */
PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST
,
/* seekProcessed= */
true
);
/* seekProcessed= */
true
,
/* positionDiscontinuity= */
true
,
/* positionDiscontinuityReason= */
DISCONTINUITY_REASON_SEEK
,
/* discontinuityWindowStartPositionUs= */
getCurrentPositionUsInternal
(
newPlaybackInfo
),
oldMaskingWindowIndex
);
}
@Override
...
...
@@ -685,11 +702,13 @@ import java.util.concurrent.CopyOnWriteArraySet;
internalPlayer
.
setPlaybackParameters
(
playbackParameters
);
updatePlaybackInfo
(
newPlaybackInfo
,
/* positionDiscontinuity= */
false
,
/* ignored */
DISCONTINUITY_REASON_INTERNAL
,
/* ignored */
TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED
,
/* ignored */
PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST
,
/* seekProcessed= */
false
);
/* seekProcessed= */
false
,
/* positionDiscontinuity= */
false
,
/* ignored */
DISCONTINUITY_REASON_INTERNAL
,
/* ignored */
C
.
TIME_UNSET
,
/* ignored */
C
.
INDEX_UNSET
);
}
@Override
...
...
@@ -758,13 +777,17 @@ import java.util.concurrent.CopyOnWriteArraySet;
}
pendingOperationAcks
++;
internalPlayer
.
stop
();
boolean
positionDiscontinuity
=
playbackInfo
.
timeline
.
isEmpty
()
&&
!
this
.
playbackInfo
.
timeline
.
isEmpty
();
updatePlaybackInfo
(
playbackInfo
,
/* positionDiscontinuity= */
false
,
/* ignored */
DISCONTINUITY_REASON_INTERNAL
,
TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED
,
/* ignored */
PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST
,
/* seekProcessed= */
false
);
/* seekProcessed= */
false
,
positionDiscontinuity
,
DISCONTINUITY_REASON_REMOVE
,
/* discontinuityWindowStartPositionUs= */
getCurrentPositionUsInternal
(
playbackInfo
),
/* ignored */
C
.
INDEX_UNSET
);
}
@Override
...
...
@@ -839,13 +862,7 @@ import java.util.concurrent.CopyOnWriteArraySet;
@Override
public
long
getCurrentPosition
()
{
if
(
playbackInfo
.
timeline
.
isEmpty
())
{
return
maskingWindowPositionMs
;
}
else
if
(
playbackInfo
.
periodId
.
isAd
())
{
return
C
.
usToMs
(
playbackInfo
.
positionUs
);
}
else
{
return
periodPositionUsToWindowPositionMs
(
playbackInfo
.
periodId
,
playbackInfo
.
positionUs
);
}
return
C
.
usToMs
(
getCurrentPositionUsInternal
(
playbackInfo
));
}
@Override
...
...
@@ -909,8 +926,9 @@ import java.util.concurrent.CopyOnWriteArraySet;
contentBufferedPositionUs
=
loadingPeriod
.
durationUs
;
}
}
return
periodPositionUsToWindowPositionMs
(
playbackInfo
.
loadingMediaPeriodId
,
contentBufferedPositionUs
);
return
C
.
usToMs
(
periodPositionUsToWindowPositionUs
(
playbackInfo
.
timeline
,
playbackInfo
.
loadingMediaPeriodId
,
contentBufferedPositionUs
));
}
@Override
...
...
@@ -958,6 +976,17 @@ import java.util.concurrent.CopyOnWriteArraySet;
}
}
private
long
getCurrentPositionUsInternal
(
PlaybackInfo
playbackInfo
)
{
if
(
playbackInfo
.
timeline
.
isEmpty
())
{
return
C
.
msToUs
(
maskingWindowPositionMs
);
}
else
if
(
playbackInfo
.
periodId
.
isAd
())
{
return
playbackInfo
.
positionUs
;
}
else
{
return
periodPositionUsToWindowPositionUs
(
playbackInfo
.
timeline
,
playbackInfo
.
periodId
,
playbackInfo
.
positionUs
);
}
}
private
List
<
MediaSource
>
createMediaSources
(
List
<
MediaItem
>
mediaItems
)
{
List
<
MediaSource
>
mediaSources
=
new
ArrayList
<>();
for
(
int
i
=
0
;
i
<
mediaItems
.
size
();
i
++)
{
...
...
@@ -969,8 +998,8 @@ import java.util.concurrent.CopyOnWriteArraySet;
private
void
handlePlaybackInfo
(
ExoPlayerImplInternal
.
PlaybackInfoUpdate
playbackInfoUpdate
)
{
pendingOperationAcks
-=
playbackInfoUpdate
.
operationAcks
;
if
(
playbackInfoUpdate
.
positionDiscontinuity
)
{
hasPendingDiscontinuity
=
true
;
pendingDiscontinuityReason
=
playbackInfoUpdate
.
discontinuityReason
;
pendingDiscontinuity
=
true
;
}
if
(
playbackInfoUpdate
.
hasPlayWhenReadyChangeReason
)
{
pendingPlayWhenReadyChangeReason
=
playbackInfoUpdate
.
playWhenReadyChangeReason
;
...
...
@@ -991,15 +1020,33 @@ import java.util.concurrent.CopyOnWriteArraySet;
mediaSourceHolderSnapshots
.
get
(
i
).
timeline
=
timelines
.
get
(
i
);
}
}
boolean
positionDiscontinuity
=
hasPendingDiscontinuity
;
hasPendingDiscontinuity
=
false
;
boolean
positionDiscontinuity
=
false
;
long
discontinuityWindowStartPositionUs
=
C
.
TIME_UNSET
;
if
(
pendingDiscontinuity
)
{
positionDiscontinuity
=
!
playbackInfoUpdate
.
playbackInfo
.
periodId
.
equals
(
playbackInfo
.
periodId
)
||
playbackInfoUpdate
.
playbackInfo
.
discontinuityStartPositionUs
!=
playbackInfo
.
positionUs
;
if
(
positionDiscontinuity
)
{
discontinuityWindowStartPositionUs
=
newTimeline
.
isEmpty
()
||
playbackInfoUpdate
.
playbackInfo
.
periodId
.
isAd
()
?
playbackInfoUpdate
.
playbackInfo
.
discontinuityStartPositionUs
:
periodPositionUsToWindowPositionUs
(
newTimeline
,
playbackInfoUpdate
.
playbackInfo
.
periodId
,
playbackInfoUpdate
.
playbackInfo
.
discontinuityStartPositionUs
);
}
}
pendingDiscontinuity
=
false
;
updatePlaybackInfo
(
playbackInfoUpdate
.
playbackInfo
,
positionDiscontinuity
,
pendingDiscontinuityReason
,
TIMELINE_CHANGE_REASON_SOURCE_UPDATE
,
pendingPlayWhenReadyChangeReason
,
/* seekProcessed= */
false
);
/* seekProcessed= */
false
,
positionDiscontinuity
,
pendingDiscontinuityReason
,
discontinuityWindowStartPositionUs
,
/* ignored */
C
.
INDEX_UNSET
);
}
}
...
...
@@ -1007,11 +1054,14 @@ import java.util.concurrent.CopyOnWriteArraySet;
@SuppressWarnings
(
"deprecation"
)
private
void
updatePlaybackInfo
(
PlaybackInfo
playbackInfo
,
boolean
positionDiscontinuity
,
@DiscontinuityReason
int
positionDiscontinuityReason
,
@TimelineChangeReason
int
timelineChangeReason
,
@PlayWhenReadyChangeReason
int
playWhenReadyChangeReason
,
boolean
seekProcessed
)
{
boolean
seekProcessed
,
boolean
positionDiscontinuity
,
@DiscontinuityReason
int
positionDiscontinuityReason
,
long
discontinuityWindowStartPositionUs
,
int
oldMaskingWindowIndex
)
{
// Assign playback info immediately such that all getters return the right values, but keep
// snapshot of previous and new state so that listener invocations are triggered correctly.
PlaybackInfo
previousPlaybackInfo
=
this
.
playbackInfo
;
...
...
@@ -1042,9 +1092,17 @@ import java.util.concurrent.CopyOnWriteArraySet;
});
}
if
(
positionDiscontinuity
)
{
PositionInfo
previousPositionInfo
=
getPreviousPositionInfo
(
positionDiscontinuityReason
,
previousPlaybackInfo
,
oldMaskingWindowIndex
);
PositionInfo
positionInfo
=
getPositionInfo
(
discontinuityWindowStartPositionUs
);
listeners
.
queueEvent
(
Player
.
EVENT_POSITION_DISCONTINUITY
,
listener
->
listener
.
onPositionDiscontinuity
(
positionDiscontinuityReason
));
listener
->
{
listener
.
onPositionDiscontinuity
(
positionDiscontinuityReason
);
listener
.
onPositionDiscontinuity
(
previousPositionInfo
,
positionInfo
,
positionDiscontinuityReason
);
});
}
if
(
mediaItemTransitioned
)
{
@Nullable
final
MediaItem
mediaItem
;
...
...
@@ -1144,6 +1202,93 @@ import java.util.concurrent.CopyOnWriteArraySet;
}
}
private
PositionInfo
getPreviousPositionInfo
(
@DiscontinuityReason
int
positionDiscontinuityReason
,
PlaybackInfo
oldPlaybackInfo
,
int
oldMaskingWindowIndex
)
{
@Nullable
Object
oldWindowUid
=
null
;
@Nullable
Object
oldPeriodUid
=
null
;
int
oldWindowIndex
=
oldMaskingWindowIndex
;
int
oldPeriodIndex
=
C
.
INDEX_UNSET
;
Timeline
.
Period
oldPeriod
=
new
Timeline
.
Period
();
if
(!
oldPlaybackInfo
.
timeline
.
isEmpty
())
{
oldPeriodUid
=
oldPlaybackInfo
.
periodId
.
periodUid
;
oldPlaybackInfo
.
timeline
.
getPeriodByUid
(
oldPeriodUid
,
oldPeriod
);
oldWindowIndex
=
oldPeriod
.
windowIndex
;
oldPeriodIndex
=
oldPlaybackInfo
.
timeline
.
getIndexOfPeriod
(
oldPeriodUid
);
oldWindowUid
=
oldPlaybackInfo
.
timeline
.
getWindow
(
oldWindowIndex
,
window
).
uid
;
}
long
oldPositionUs
;
long
oldContentPositionUs
;
if
(
positionDiscontinuityReason
==
DISCONTINUITY_REASON_AUTO_TRANSITION
)
{
oldPositionUs
=
oldPeriod
.
positionInWindowUs
+
oldPeriod
.
durationUs
;
oldContentPositionUs
=
oldPositionUs
;
if
(
oldPlaybackInfo
.
periodId
.
isAd
())
{
// The old position is the end of the previous ad.
oldPositionUs
=
oldPeriod
.
getAdDurationUs
(
oldPlaybackInfo
.
periodId
.
adGroupIndex
,
oldPlaybackInfo
.
periodId
.
adIndexInAdGroup
);
// The ad cue point is stored in the old requested content position.
oldContentPositionUs
=
getRequestedContentPositionUs
(
oldPlaybackInfo
);
}
else
if
(
oldPlaybackInfo
.
periodId
.
nextAdGroupIndex
!=
C
.
INDEX_UNSET
&&
playbackInfo
.
periodId
.
isAd
())
{
// If it's a transition from content to an ad in the same window, the old position is the
// ad cue point that is the same as current content position.
oldPositionUs
=
getRequestedContentPositionUs
(
playbackInfo
);
oldContentPositionUs
=
oldPositionUs
;
}
}
else
if
(
oldPlaybackInfo
.
periodId
.
isAd
())
{
oldPositionUs
=
oldPlaybackInfo
.
positionUs
;
oldContentPositionUs
=
getRequestedContentPositionUs
(
oldPlaybackInfo
);
}
else
{
oldPositionUs
=
oldPeriod
.
positionInWindowUs
+
oldPlaybackInfo
.
positionUs
;
oldContentPositionUs
=
oldPositionUs
;
}
return
new
PositionInfo
(
oldWindowUid
,
oldWindowIndex
,
oldPeriodUid
,
oldPeriodIndex
,
C
.
usToMs
(
oldPositionUs
),
C
.
usToMs
(
oldContentPositionUs
),
oldPlaybackInfo
.
periodId
.
adGroupIndex
,
oldPlaybackInfo
.
periodId
.
adIndexInAdGroup
);
}
private
PositionInfo
getPositionInfo
(
long
discontinuityWindowStartPositionUs
)
{
@Nullable
Object
newWindowUid
=
null
;
@Nullable
Object
newPeriodUid
=
null
;
int
newWindowIndex
=
getCurrentWindowIndex
();
int
newPeriodIndex
=
C
.
INDEX_UNSET
;
if
(!
playbackInfo
.
timeline
.
isEmpty
())
{
newPeriodUid
=
playbackInfo
.
periodId
.
periodUid
;
playbackInfo
.
timeline
.
getPeriodByUid
(
newPeriodUid
,
period
);
newPeriodIndex
=
playbackInfo
.
timeline
.
getIndexOfPeriod
(
newPeriodUid
);
newWindowUid
=
playbackInfo
.
timeline
.
getWindow
(
newWindowIndex
,
window
).
uid
;
}
long
positionMs
=
C
.
usToMs
(
discontinuityWindowStartPositionUs
);
return
new
PositionInfo
(
newWindowUid
,
newWindowIndex
,
newPeriodUid
,
newPeriodIndex
,
positionMs
,
/* contentPositionMs= */
playbackInfo
.
periodId
.
isAd
()
?
C
.
usToMs
(
getRequestedContentPositionUs
(
playbackInfo
))
:
positionMs
,
playbackInfo
.
periodId
.
adGroupIndex
,
playbackInfo
.
periodId
.
adIndexInAdGroup
);
}
private
static
long
getRequestedContentPositionUs
(
PlaybackInfo
playbackInfo
)
{
Timeline
.
Window
window
=
new
Timeline
.
Window
();
Timeline
.
Period
period
=
new
Timeline
.
Period
();
playbackInfo
.
timeline
.
getPeriodByUid
(
playbackInfo
.
periodId
.
periodUid
,
period
);
return
playbackInfo
.
requestedContentPositionUs
==
C
.
TIME_UNSET
?
playbackInfo
.
timeline
.
getWindow
(
period
.
windowIndex
,
window
).
getDefaultPositionUs
()
:
period
.
getPositionInWindowUs
()
+
playbackInfo
.
requestedContentPositionUs
;
}
private
Pair
<
Boolean
,
Integer
>
evaluateMediaItemTransitionReason
(
PlaybackInfo
playbackInfo
,
PlaybackInfo
oldPlaybackInfo
,
...
...
@@ -1169,7 +1314,7 @@ import java.util.concurrent.CopyOnWriteArraySet;
if
(!
oldWindowUid
.
equals
(
newWindowUid
))
{
@Player
.
MediaItemTransitionReason
int
transitionReason
;
if
(
positionDiscontinuity
&&
positionDiscontinuityReason
==
DISCONTINUITY_REASON_
PERIOD
_TRANSITION
)
{
&&
positionDiscontinuityReason
==
DISCONTINUITY_REASON_
AUTO
_TRANSITION
)
{
transitionReason
=
MEDIA_ITEM_TRANSITION_REASON_AUTO
;
}
else
if
(
positionDiscontinuity
&&
positionDiscontinuityReason
==
DISCONTINUITY_REASON_SEEK
)
{
...
...
@@ -1182,7 +1327,7 @@ import java.util.concurrent.CopyOnWriteArraySet;
}
return
new
Pair
<>(
/* isTransitioning */
true
,
transitionReason
);
}
else
if
(
positionDiscontinuity
&&
positionDiscontinuityReason
==
DISCONTINUITY_REASON_
PERIOD
_TRANSITION
&&
positionDiscontinuityReason
==
DISCONTINUITY_REASON_
AUTO
_TRANSITION
&&
newTimeline
.
getIndexOfPeriod
(
playbackInfo
.
periodId
.
periodUid
)
==
firstPeriodIndexInNewWindow
)
{
return
new
Pair
<>(
/* isTransitioning */
true
,
MEDIA_ITEM_TRANSITION_REASON_REPEAT
);
...
...
@@ -1245,13 +1390,18 @@ import java.util.concurrent.CopyOnWriteArraySet;
newPlaybackInfo
=
newPlaybackInfo
.
copyWithPlaybackState
(
maskingPlaybackState
);
internalPlayer
.
setMediaSources
(
holders
,
startWindowIndex
,
C
.
msToUs
(
startPositionMs
),
shuffleOrder
);
boolean
positionDiscontinuity
=
!
playbackInfo
.
periodId
.
periodUid
.
equals
(
newPlaybackInfo
.
periodId
.
periodUid
)
&&
!
playbackInfo
.
timeline
.
isEmpty
();
updatePlaybackInfo
(
newPlaybackInfo
,
/* positionDiscontinuity= */
false
,
/* ignored */
Player
.
DISCONTINUITY_REASON_INTERNAL
,
/* timelineChangeReason= */
TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED
,
/* ignored */
PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST
,
/* seekProcessed= */
false
);
/* seekProcessed= */
false
,
/* positionDiscontinuity= */
positionDiscontinuity
,
Player
.
DISCONTINUITY_REASON_REMOVE
,
/* discontinuityWindowStartPositionUs= */
getCurrentPositionUsInternal
(
newPlaybackInfo
),
/* ignored */
C
.
INDEX_UNSET
);
}
private
List
<
MediaSourceList
.
MediaSourceHolder
>
addMediaSourceHolders
(
...
...
@@ -1319,11 +1469,13 @@ import java.util.concurrent.CopyOnWriteArraySet;
if
(
timeline
.
isEmpty
())
{
// Reset periodId and loadingPeriodId.
MediaPeriodId
dummyMediaPeriodId
=
PlaybackInfo
.
getDummyPeriodForEmptyTimeline
();
long
positionUs
=
C
.
msToUs
(
maskingWindowPositionMs
);
playbackInfo
=
playbackInfo
.
copyWithNewPosition
(
dummyMediaPeriodId
,
/* positionUs= */
C
.
msToUs
(
maskingWindowPositionMs
),
/* requestedContentPositionUs= */
C
.
msToUs
(
maskingWindowPositionMs
),
positionUs
,
/* requestedContentPositionUs= */
positionUs
,
/* discontinuityStartPositionUs= */
positionUs
,
/* totalBufferedDurationUs= */
0
,
TrackGroupArray
.
EMPTY
,
emptyTrackSelectorResult
,
...
...
@@ -1352,6 +1504,7 @@ import java.util.concurrent.CopyOnWriteArraySet;
newPeriodId
,
/* positionUs= */
newContentPositionUs
,
/* requestedContentPositionUs= */
newContentPositionUs
,
/* discontinuityStartPositionUs= */
newContentPositionUs
,
/* totalBufferedDurationUs= */
0
,
playingPeriodChanged
?
TrackGroupArray
.
EMPTY
:
playbackInfo
.
trackGroups
,
playingPeriodChanged
?
emptyTrackSelectorResult
:
playbackInfo
.
trackSelectorResult
,
...
...
@@ -1377,6 +1530,7 @@ import java.util.concurrent.CopyOnWriteArraySet;
newPeriodId
,
/* positionUs= */
playbackInfo
.
positionUs
,
/* requestedContentPositionUs= */
playbackInfo
.
positionUs
,
playbackInfo
.
discontinuityStartPositionUs
,
/* totalBufferedDurationUs= */
maskedBufferedPositionUs
-
playbackInfo
.
positionUs
,
playbackInfo
.
trackGroups
,
playbackInfo
.
trackSelectorResult
,
...
...
@@ -1400,6 +1554,7 @@ import java.util.concurrent.CopyOnWriteArraySet;
newPeriodId
,
/* positionUs= */
newContentPositionUs
,
/* requestedContentPositionUs= */
newContentPositionUs
,
/* discontinuityStartPositionUs= */
newContentPositionUs
,
maskedTotalBufferedDurationUs
,
playbackInfo
.
trackGroups
,
playbackInfo
.
trackSelectorResult
,
...
...
@@ -1468,11 +1623,11 @@ import java.util.concurrent.CopyOnWriteArraySet;
return
timeline
.
getPeriodPosition
(
window
,
period
,
windowIndex
,
C
.
msToUs
(
windowPositionMs
));
}
private
long
periodPositionUsToWindowPosition
Ms
(
MediaPeriodId
periodId
,
long
positionUs
)
{
long
positionMs
=
C
.
usToMs
(
positionUs
);
playbackInfo
.
timeline
.
getPeriodByUid
(
periodId
.
periodUid
,
period
);
position
Ms
+=
period
.
getPositionInWindowM
s
();
return
position
M
s
;
private
long
periodPositionUsToWindowPosition
Us
(
Timeline
timeline
,
MediaPeriodId
periodId
,
long
positionUs
)
{
timeline
.
getPeriodByUid
(
periodId
.
periodUid
,
period
);
position
Us
+=
period
.
getPositionInWindowU
s
();
return
position
U
s
;
}
private
static
boolean
isPlaying
(
PlaybackInfo
playbackInfo
)
{
...
...
library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java
View file @
dc4148d5
...
...
@@ -668,7 +668,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
mediaSourceList
.
setMediaSources
(
mediaSourceListUpdateMessage
.
mediaSourceHolders
,
mediaSourceListUpdateMessage
.
shuffleOrder
);
handleMediaSourceListInfoRefreshed
(
timeline
);
handleMediaSourceListInfoRefreshed
(
timeline
,
/* isSourceRefresh= */
false
);
}
private
void
addMediaItemsInternal
(
MediaSourceListUpdateMessage
addMessage
,
int
insertionIndex
)
...
...
@@ -679,7 +679,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
insertionIndex
==
C
.
INDEX_UNSET
?
mediaSourceList
.
getSize
()
:
insertionIndex
,
addMessage
.
mediaSourceHolders
,
addMessage
.
shuffleOrder
);
handleMediaSourceListInfoRefreshed
(
timeline
);
handleMediaSourceListInfoRefreshed
(
timeline
,
/* isSourceRefresh= */
false
);
}
private
void
moveMediaItemsInternal
(
MoveMediaItemsMessage
moveMediaItemsMessage
)
...
...
@@ -691,24 +691,25 @@ import java.util.concurrent.atomic.AtomicBoolean;
moveMediaItemsMessage
.
toIndex
,
moveMediaItemsMessage
.
newFromIndex
,
moveMediaItemsMessage
.
shuffleOrder
);
handleMediaSourceListInfoRefreshed
(
timeline
);
handleMediaSourceListInfoRefreshed
(
timeline
,
/* isSourceRefresh= */
false
);
}
private
void
removeMediaItemsInternal
(
int
fromIndex
,
int
toIndex
,
ShuffleOrder
shuffleOrder
)
throws
ExoPlaybackException
{
playbackInfoUpdate
.
incrementPendingOperationAcks
(
/* operationAcks= */
1
);
Timeline
timeline
=
mediaSourceList
.
removeMediaSourceRange
(
fromIndex
,
toIndex
,
shuffleOrder
);
handleMediaSourceListInfoRefreshed
(
timeline
);
handleMediaSourceListInfoRefreshed
(
timeline
,
/* isSourceRefresh= */
false
);
}
private
void
mediaSourceListUpdateRequestedInternal
()
throws
ExoPlaybackException
{
handleMediaSourceListInfoRefreshed
(
mediaSourceList
.
createTimeline
());
handleMediaSourceListInfoRefreshed
(
mediaSourceList
.
createTimeline
(),
/* isSourceRefresh= */
true
);
}
private
void
setShuffleOrderInternal
(
ShuffleOrder
shuffleOrder
)
throws
ExoPlaybackException
{
playbackInfoUpdate
.
incrementPendingOperationAcks
(
/* operationAcks= */
1
);
Timeline
timeline
=
mediaSourceList
.
setShuffleOrder
(
shuffleOrder
);
handleMediaSourceListInfoRefreshed
(
timeline
);
handleMediaSourceListInfoRefreshed
(
timeline
,
/* isSourceRefresh= */
false
);
}
private
void
notifyTrackSelectionPlayWhenReadyChanged
(
boolean
playWhenReady
)
{
...
...
@@ -803,10 +804,12 @@ import java.util.concurrent.atomic.AtomicBoolean;
if
(
newPositionUs
!=
playbackInfo
.
positionUs
)
{
playbackInfo
=
handlePositionDiscontinuity
(
periodId
,
newPositionUs
,
playbackInfo
.
requestedContentPositionUs
);
if
(
sendDiscontinuity
)
{
playbackInfoUpdate
.
setPositionDiscontinuity
(
Player
.
DISCONTINUITY_REASON_INTERNAL
);
}
periodId
,
newPositionUs
,
playbackInfo
.
requestedContentPositionUs
,
playbackInfo
.
discontinuityStartPositionUs
,
sendDiscontinuity
,
Player
.
DISCONTINUITY_REASON_INTERNAL
);
}
}
...
...
@@ -852,9 +855,11 @@ import java.util.concurrent.atomic.AtomicBoolean;
playbackInfo
=
handlePositionDiscontinuity
(
playbackInfo
.
periodId
,
discontinuityPositionUs
,
playbackInfo
.
requestedContentPositionUs
);
playbackInfoUpdate
.
setPositionDiscontinuity
(
Player
.
DISCONTINUITY_REASON_INTERNAL
);
/* positionUs= */
discontinuityPositionUs
,
playbackInfo
.
requestedContentPositionUs
,
/* discontinuityStartPositionUs= */
discontinuityPositionUs
,
/* reportDiscontinuity= */
true
,
Player
.
DISCONTINUITY_REASON_INTERNAL
);
}
}
else
{
rendererPositionUs
=
...
...
@@ -1166,10 +1171,13 @@ import java.util.concurrent.atomic.AtomicBoolean;
}
}
finally
{
playbackInfo
=
handlePositionDiscontinuity
(
periodId
,
periodPositionUs
,
requestedContentPositionUs
);
if
(
seekPositionAdjusted
)
{
playbackInfoUpdate
.
setPositionDiscontinuity
(
Player
.
DISCONTINUITY_REASON_SEEK_ADJUSTMENT
);
}
handlePositionDiscontinuity
(
periodId
,
periodPositionUs
,
requestedContentPositionUs
,
/* discontinuityStartPositionUs= */
periodPositionUs
,
/* reportDiscontinuity= */
seekPositionAdjusted
,
Player
.
DISCONTINUITY_REASON_SEEK_ADJUSTMENT
);
}
}
...
...
@@ -1385,6 +1393,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
playbackInfo
.
timeline
,
mediaPeriodId
,
requestedContentPositionUs
,
/* discontinuityStartPositionUs= */
startPositionUs
,
playbackInfo
.
playbackState
,
resetError
?
null
:
playbackInfo
.
playbackError
,
/* isLoading= */
false
,
...
...
@@ -1395,9 +1404,9 @@ import java.util.concurrent.atomic.AtomicBoolean;
playbackInfo
.
playWhenReady
,
playbackInfo
.
playbackSuppressionReason
,
playbackInfo
.
playbackParameters
,
startPositionUs
,
/* bufferedPositionUs= */
startPositionUs
,
/* totalBufferedDurationUs= */
0
,
startPositionUs
,
/* positionUs= */
startPositionUs
,
offloadSchedulingEnabled
,
/* sleepingForOffload= */
false
);
if
(
releaseMediaSourceList
)
{
...
...
@@ -1634,12 +1643,18 @@ import java.util.concurrent.atomic.AtomicBoolean;
long
periodPositionUs
=
playingPeriodHolder
.
applyTrackSelection
(
newTrackSelectorResult
,
playbackInfo
.
positionUs
,
recreateStreams
,
streamResetFlags
);
boolean
hasDiscontinuity
=
playbackInfo
.
playbackState
!=
Player
.
STATE_ENDED
&&
periodPositionUs
!=
playbackInfo
.
positionUs
;
playbackInfo
=
handlePositionDiscontinuity
(
playbackInfo
.
periodId
,
periodPositionUs
,
playbackInfo
.
requestedContentPositionUs
);
if
(
playbackInfo
.
playbackState
!=
Player
.
STATE_ENDED
&&
periodPositionUs
!=
playbackInfo
.
positionUs
)
{
playbackInfoUpdate
.
setPositionDiscontinuity
(
Player
.
DISCONTINUITY_REASON_INTERNAL
);
playbackInfo
.
periodId
,
periodPositionUs
,
playbackInfo
.
requestedContentPositionUs
,
playbackInfo
.
discontinuityStartPositionUs
,
hasDiscontinuity
,
Player
.
DISCONTINUITY_REASON_INTERNAL
);
if
(
hasDiscontinuity
)
{
resetRendererPosition
(
periodPositionUs
);
}
...
...
@@ -1742,7 +1757,8 @@ import java.util.concurrent.atomic.AtomicBoolean;
||
!
shouldPlayWhenReady
());
}
private
void
handleMediaSourceListInfoRefreshed
(
Timeline
timeline
)
throws
ExoPlaybackException
{
private
void
handleMediaSourceListInfoRefreshed
(
Timeline
timeline
,
boolean
isSourceRefresh
)
throws
ExoPlaybackException
{
PositionUpdateForPlaylistChange
positionUpdate
=
resolvePositionForPlaylistChange
(
timeline
,
...
...
@@ -1759,7 +1775,6 @@ import java.util.concurrent.atomic.AtomicBoolean;
long
newPositionUs
=
positionUpdate
.
periodPositionUs
;
boolean
periodPositionChanged
=
!
playbackInfo
.
periodId
.
equals
(
newPeriodId
)
||
newPositionUs
!=
playbackInfo
.
positionUs
;
try
{
if
(
positionUpdate
.
endPlayback
)
{
if
(
playbackInfo
.
playbackState
!=
Player
.
STATE_IDLE
)
{
...
...
@@ -1800,8 +1815,25 @@ import java.util.concurrent.atomic.AtomicBoolean;
:
C
.
TIME_UNSET
);
if
(
periodPositionChanged
||
newRequestedContentPositionUs
!=
playbackInfo
.
requestedContentPositionUs
)
{
Object
oldPeriodUid
=
playbackInfo
.
periodId
.
periodUid
;
Timeline
oldTimeline
=
playbackInfo
.
timeline
;
boolean
reportDiscontinuity
=
periodPositionChanged
&&
isSourceRefresh
&&
!
oldTimeline
.
isEmpty
()
&&
!
oldTimeline
.
getWindow
(
oldTimeline
.
getPeriodByUid
(
oldPeriodUid
,
period
).
windowIndex
,
window
)
.
isPlaceholder
;
playbackInfo
=
handlePositionDiscontinuity
(
newPeriodId
,
newPositionUs
,
newRequestedContentPositionUs
);
handlePositionDiscontinuity
(
newPeriodId
,
newPositionUs
,
newRequestedContentPositionUs
,
playbackInfo
.
discontinuityStartPositionUs
,
reportDiscontinuity
,
timeline
.
getIndexOfPeriod
(
oldPeriodUid
)
==
C
.
INDEX_UNSET
?
Player
.
DISCONTINUITY_REASON_REMOVE
:
Player
.
DISCONTINUITY_REASON_SKIP
);
}
resetPendingPauseAtEndOfPeriod
();
resolvePendingMessagePositions
(
...
...
@@ -2049,12 +2081,10 @@ import java.util.concurrent.atomic.AtomicBoolean;
handlePositionDiscontinuity
(
newPlayingPeriodHolder
.
info
.
id
,
newPlayingPeriodHolder
.
info
.
startPositionUs
,
newPlayingPeriodHolder
.
info
.
requestedContentPositionUs
);
int
discontinuityReason
=
oldPlayingPeriodHolder
.
info
.
isLastInTimelinePeriod
?
Player
.
DISCONTINUITY_REASON_PERIOD_TRANSITION
:
Player
.
DISCONTINUITY_REASON_AD_INSERTION
;
playbackInfoUpdate
.
setPositionDiscontinuity
(
discontinuityReason
);
newPlayingPeriodHolder
.
info
.
requestedContentPositionUs
,
/* discontinuityStartPositionUs= */
newPlayingPeriodHolder
.
info
.
startPositionUs
,
/* reportDiscontinuity= */
true
,
Player
.
DISCONTINUITY_REASON_AUTO_TRANSITION
);
updateLivePlaybackSpeedControl
(
/* newTimeline= */
playbackInfo
.
timeline
,
/* newPeriodId= */
newPlayingPeriodHolder
.
info
.
id
,
...
...
@@ -2140,7 +2170,10 @@ import java.util.concurrent.atomic.AtomicBoolean;
handlePositionDiscontinuity
(
playbackInfo
.
periodId
,
loadingPeriodHolder
.
info
.
startPositionUs
,
playbackInfo
.
requestedContentPositionUs
);
playbackInfo
.
requestedContentPositionUs
,
loadingPeriodHolder
.
info
.
startPositionUs
,
/* reportDiscontinuity= */
false
,
/* ignored */
Player
.
DISCONTINUITY_REASON_INTERNAL
);
}
maybeContinueLoading
();
}
...
...
@@ -2232,7 +2265,12 @@ import java.util.concurrent.atomic.AtomicBoolean;
@CheckResult
private
PlaybackInfo
handlePositionDiscontinuity
(
MediaPeriodId
mediaPeriodId
,
long
positionUs
,
long
contentPositionUs
)
{
MediaPeriodId
mediaPeriodId
,
long
positionUs
,
long
contentPositionUs
,
long
discontinuityStartPositionUs
,
boolean
reportDiscontinuity
,
@DiscontinuityReason
int
discontinuityReason
)
{
deliverPendingMessageAtStartPositionRequired
=
deliverPendingMessageAtStartPositionRequired
||
positionUs
!=
playbackInfo
.
positionUs
...
...
@@ -2264,11 +2302,14 @@ import java.util.concurrent.atomic.AtomicBoolean;
trackSelectorResult
=
emptyTrackSelectorResult
;
staticMetadata
=
ImmutableList
.
of
();
}
if
(
reportDiscontinuity
)
{
playbackInfoUpdate
.
setPositionDiscontinuity
(
discontinuityReason
);
}
return
playbackInfo
.
copyWithNewPosition
(
mediaPeriodId
,
positionUs
,
contentPositionUs
,
discontinuityStartPositionUs
,
getTotalBufferedDurationUs
(),
trackGroupArray
,
trackSelectorResult
,
...
...
library/core/src/main/java/com/google/android/exoplayer2/PlaybackInfo.java
View file @
dc4148d5
...
...
@@ -50,6 +50,8 @@ import java.util.List;
* suspended content.
*/
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. */
@Player
.
State
public
final
int
playbackState
;
/** The current playback error, or null if this is not an error state. */
...
...
@@ -104,6 +106,7 @@ import java.util.List;
Timeline
.
EMPTY
,
PLACEHOLDER_MEDIA_PERIOD_ID
,
/* requestedContentPositionUs= */
C
.
TIME_UNSET
,
/* discontinuityStartPositionUs= */
0
,
Player
.
STATE_IDLE
,
/* playbackError= */
null
,
/* isLoading= */
false
,
...
...
@@ -147,6 +150,7 @@ import java.util.List;
Timeline
timeline
,
MediaPeriodId
periodId
,
long
requestedContentPositionUs
,
long
discontinuityStartPositionUs
,
@Player
.
State
int
playbackState
,
@Nullable
ExoPlaybackException
playbackError
,
boolean
isLoading
,
...
...
@@ -165,6 +169,7 @@ import java.util.List;
this
.
timeline
=
timeline
;
this
.
periodId
=
periodId
;
this
.
requestedContentPositionUs
=
requestedContentPositionUs
;
this
.
discontinuityStartPositionUs
=
discontinuityStartPositionUs
;
this
.
playbackState
=
playbackState
;
this
.
playbackError
=
playbackError
;
this
.
isLoading
=
isLoading
;
...
...
@@ -207,6 +212,7 @@ import java.util.List;
MediaPeriodId
periodId
,
long
positionUs
,
long
requestedContentPositionUs
,
long
discontinuityStartPositionUs
,
long
totalBufferedDurationUs
,
TrackGroupArray
trackGroups
,
TrackSelectorResult
trackSelectorResult
,
...
...
@@ -215,6 +221,7 @@ import java.util.List;
timeline
,
periodId
,
requestedContentPositionUs
,
discontinuityStartPositionUs
,
playbackState
,
playbackError
,
isLoading
,
...
...
@@ -244,6 +251,7 @@ import java.util.List;
timeline
,
periodId
,
requestedContentPositionUs
,
discontinuityStartPositionUs
,
playbackState
,
playbackError
,
isLoading
,
...
...
@@ -273,6 +281,7 @@ import java.util.List;
timeline
,
periodId
,
requestedContentPositionUs
,
discontinuityStartPositionUs
,
playbackState
,
playbackError
,
isLoading
,
...
...
@@ -302,6 +311,7 @@ import java.util.List;
timeline
,
periodId
,
requestedContentPositionUs
,
discontinuityStartPositionUs
,
playbackState
,
playbackError
,
isLoading
,
...
...
@@ -331,6 +341,7 @@ import java.util.List;
timeline
,
periodId
,
requestedContentPositionUs
,
discontinuityStartPositionUs
,
playbackState
,
playbackError
,
isLoading
,
...
...
@@ -360,6 +371,7 @@ import java.util.List;
timeline
,
periodId
,
requestedContentPositionUs
,
discontinuityStartPositionUs
,
playbackState
,
playbackError
,
isLoading
,
...
...
@@ -393,6 +405,7 @@ import java.util.List;
timeline
,
periodId
,
requestedContentPositionUs
,
discontinuityStartPositionUs
,
playbackState
,
playbackError
,
isLoading
,
...
...
@@ -422,6 +435,7 @@ import java.util.List;
timeline
,
periodId
,
requestedContentPositionUs
,
discontinuityStartPositionUs
,
playbackState
,
playbackError
,
isLoading
,
...
...
@@ -452,6 +466,7 @@ import java.util.List;
timeline
,
periodId
,
requestedContentPositionUs
,
discontinuityStartPositionUs
,
playbackState
,
playbackError
,
isLoading
,
...
...
@@ -481,6 +496,7 @@ import java.util.List;
timeline
,
periodId
,
requestedContentPositionUs
,
discontinuityStartPositionUs
,
playbackState
,
playbackError
,
isLoading
,
...
...
library/core/src/main/java/com/google/android/exoplayer2/analytics/AnalyticsCollector.java
View file @
dc4148d5
...
...
@@ -706,8 +706,13 @@ public class AnalyticsCollector
listener
->
listener
.
onPlayerError
(
eventTime
,
error
));
}
// Calling deprecated callback.
@SuppressWarnings
(
"deprecation"
)
@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
)
{
isSeeking
=
false
;
}
...
...
@@ -716,7 +721,10 @@ public class AnalyticsCollector
sendEvent
(
eventTime
,
AnalyticsListener
.
EVENT_POSITION_DISCONTINUITY
,
listener
->
listener
.
onPositionDiscontinuity
(
eventTime
,
reason
));
listener
->
{
listener
.
onPositionDiscontinuity
(
eventTime
,
reason
);
listener
.
onPositionDiscontinuity
(
eventTime
,
oldPosition
,
newPosition
,
reason
);
});
}
@Override
...
...
library/core/src/main/java/com/google/android/exoplayer2/analytics/AnalyticsListener.java
View file @
dc4148d5
...
...
@@ -236,7 +236,7 @@ public interface AnalyticsListener {
int
EVENT_PLAYER_ERROR
=
Player
.
EVENT_PLAYER_ERROR
;
/**
* 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
;
/** {@link Player#getPlaybackParameters()} changed. */
...
...
@@ -532,12 +532,25 @@ public interface AnalyticsListener {
@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.
*
* @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.
*/
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.
...
...
library/core/src/main/java/com/google/android/exoplayer2/analytics/DefaultPlaybackSessionManager.java
View file @
dc4148d5
...
...
@@ -191,9 +191,7 @@ public final class DefaultPlaybackSessionManager implements PlaybackSessionManag
public
synchronized
void
updateSessionsWithDiscontinuity
(
EventTime
eventTime
,
@DiscontinuityReason
int
reason
)
{
Assertions
.
checkNotNull
(
listener
);
boolean
hasAutomaticTransition
=
reason
==
Player
.
DISCONTINUITY_REASON_PERIOD_TRANSITION
||
reason
==
Player
.
DISCONTINUITY_REASON_AD_INSERTION
;
boolean
hasAutomaticTransition
=
reason
==
Player
.
DISCONTINUITY_REASON_AUTO_TRANSITION
;
Iterator
<
SessionDescriptor
>
iterator
=
sessions
.
values
().
iterator
();
while
(
iterator
.
hasNext
())
{
SessionDescriptor
session
=
iterator
.
next
();
...
...
library/core/src/main/java/com/google/android/exoplayer2/util/DebugTextViewHelper.java
View file @
dc4148d5
...
...
@@ -89,7 +89,10 @@ public class DebugTextViewHelper implements Player.EventListener, Runnable {
}
@Override
public
final
void
onPositionDiscontinuity
(
@Player
.
DiscontinuityReason
int
reason
)
{
public
final
void
onPositionDiscontinuity
(
Player
.
PositionInfo
oldPosition
,
Player
.
PositionInfo
newPosition
,
@Player
.
DiscontinuityReason
int
reason
)
{
updateAndPost
();
}
...
...
library/core/src/main/java/com/google/android/exoplayer2/util/EventLogger.java
View file @
dc4148d5
...
...
@@ -141,8 +141,50 @@ public class EventLogger implements AnalyticsListener {
}
@Override
public
void
onPositionDiscontinuity
(
EventTime
eventTime
,
@Player
.
DiscontinuityReason
int
reason
)
{
logd
(
eventTime
,
"positionDiscontinuity"
,
getDiscontinuityReasonString
(
reason
));
public
void
onPositionDiscontinuity
(
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
...
...
@@ -658,14 +700,16 @@ public class EventLogger implements AnalyticsListener {
private
static
String
getDiscontinuityReasonString
(
@Player
.
DiscontinuityReason
int
reason
)
{
switch
(
reason
)
{
case
Player
.
DISCONTINUITY_REASON_
PERIOD
_TRANSITION
:
return
"
PERIOD
_TRANSITION"
;
case
Player
.
DISCONTINUITY_REASON_
AUTO
_TRANSITION
:
return
"
AUTO
_TRANSITION"
;
case
Player
.
DISCONTINUITY_REASON_SEEK
:
return
"SEEK"
;
case
Player
.
DISCONTINUITY_REASON_SEEK_ADJUSTMENT
:
return
"SEEK_ADJUSTMENT"
;
case
Player
.
DISCONTINUITY_REASON_AD_INSERTION
:
return
"AD_INSERTION"
;
case
Player
.
DISCONTINUITY_REASON_REMOVE
:
return
"REMOVE"
;
case
Player
.
DISCONTINUITY_REASON_SKIP
:
return
"SKIP"
;
case
Player
.
DISCONTINUITY_REASON_INTERNAL
:
return
"INTERNAL"
;
default
:
...
...
library/core/src/test/java/com/google/android/exoplayer2/ExoPlayerTest.java
View file @
dc4148d5
...
...
@@ -49,6 +49,7 @@ import static com.google.common.truth.Truth.assertThat;
import
static
org
.
junit
.
Assert
.
assertArrayEquals
;
import
static
org
.
junit
.
Assert
.
assertThrows
;
import
static
org
.
junit
.
Assert
.
fail
;
import
static
org
.
mockito
.
AdditionalMatchers
.
not
;
import
static
org
.
mockito
.
ArgumentMatchers
.
any
;
import
static
org
.
mockito
.
ArgumentMatchers
.
anyBoolean
;
import
static
org
.
mockito
.
ArgumentMatchers
.
anyInt
;
...
...
@@ -132,6 +133,7 @@ import com.google.android.exoplayer2.util.Clock;
import
com.google.android.exoplayer2.util.MimeTypes
;
import
com.google.common.collect.ImmutableList
;
import
com.google.common.collect.Iterables
;
import
com.google.common.collect.Lists
;
import
com.google.common.collect.Range
;
import
java.io.IOException
;
import
java.util.ArrayList
;
...
...
@@ -210,6 +212,7 @@ public final class ExoPlayerTest {
.
onTimelineChanged
(
argThat
(
noUid
(
timeline
)),
eq
(
Player
.
TIMELINE_CHANGE_REASON_SOURCE_UPDATE
));
inOrder
.
verify
(
mockListener
,
never
()).
onPositionDiscontinuity
(
anyInt
());
inOrder
.
verify
(
mockListener
,
never
()).
onPositionDiscontinuity
(
any
(),
any
(),
anyInt
());
assertThat
(
renderer
.
getFormatsRead
()).
isEmpty
();
assertThat
(
renderer
.
sampleBufferReadCount
).
isEqualTo
(
0
);
assertThat
(
renderer
.
isEnded
).
isFalse
();
...
...
@@ -244,6 +247,7 @@ public final class ExoPlayerTest {
.
onTracksChanged
(
eq
(
new
TrackGroupArray
(
new
TrackGroup
(
ExoPlayerTestRunner
.
VIDEO_FORMAT
))),
any
());
inOrder
.
verify
(
mockListener
,
never
()).
onPositionDiscontinuity
(
anyInt
());
inOrder
.
verify
(
mockListener
,
never
()).
onPositionDiscontinuity
(
any
(),
any
(),
anyInt
());
assertThat
(
renderer
.
getFormatsRead
()).
containsExactly
(
ExoPlayerTestRunner
.
VIDEO_FORMAT
);
assertThat
(
renderer
.
sampleBufferReadCount
).
isEqualTo
(
1
);
assertThat
(
renderer
.
isEnded
).
isTrue
();
...
...
@@ -268,14 +272,14 @@ public final class ExoPlayerTest {
.
verify
(
mockEventListener
)
.
onTimelineChanged
(
argThat
(
noUid
(
new
FakeMediaSource
.
InitialTimeline
(
timeline
))),
eq
(
Player
.
DISCONTINUITY_REASON_
PERIOD
_TRANSITION
));
eq
(
Player
.
DISCONTINUITY_REASON_
AUTO
_TRANSITION
));
inOrder
.
verify
(
mockEventListener
)
.
onTimelineChanged
(
argThat
(
noUid
(
timeline
)),
eq
(
Player
.
TIMELINE_CHANGE_REASON_SOURCE_UPDATE
));
inOrder
.
verify
(
mockEventListener
,
times
(
2
))
.
onPositionDiscontinuity
(
Player
.
DISCONTINUITY_REASON_PERIOD_TRANSITION
);
.
onPositionDiscontinuity
(
any
(),
any
(),
eq
(
Player
.
DISCONTINUITY_REASON_AUTO_TRANSITION
)
);
assertThat
(
renderer
.
getFormatsRead
())
.
containsExactly
(
ExoPlayerTestRunner
.
VIDEO_FORMAT
,
...
...
@@ -313,7 +317,7 @@ public final class ExoPlayerTest {
argThat
(
noUid
(
timeline
)),
eq
(
Player
.
TIMELINE_CHANGE_REASON_SOURCE_UPDATE
));
inOrder
.
verify
(
mockEventListener
,
times
(
99
))
.
onPositionDiscontinuity
(
Player
.
DISCONTINUITY_REASON_PERIOD_TRANSITION
);
.
onPositionDiscontinuity
(
any
(),
any
(),
eq
(
Player
.
DISCONTINUITY_REASON_AUTO_TRANSITION
)
);
assertThat
(
renderer
.
getFormatsRead
()).
hasSize
(
100
);
assertThat
(
renderer
.
sampleBufferReadCount
).
isEqualTo
(
100
);
assertThat
(
renderer
.
isEnded
).
isTrue
();
...
...
@@ -397,7 +401,7 @@ public final class ExoPlayerTest {
argThat
(
noUid
(
timeline
)),
eq
(
Player
.
TIMELINE_CHANGE_REASON_SOURCE_UPDATE
));
inOrder
.
verify
(
mockEventListener
,
times
(
2
))
.
onPositionDiscontinuity
(
Player
.
DISCONTINUITY_REASON_PERIOD_TRANSITION
);
.
onPositionDiscontinuity
(
any
(),
any
(),
eq
(
Player
.
DISCONTINUITY_REASON_AUTO_TRANSITION
)
);
assertThat
(
audioRenderer
.
positionResetCount
).
isEqualTo
(
1
);
assertThat
(
videoRenderer
.
isEnded
).
isTrue
();
assertThat
(
audioRenderer
.
isEnded
).
isTrue
();
...
...
@@ -441,7 +445,6 @@ public final class ExoPlayerTest {
// second source was suppressed as we replace it with the third source before the update
// arrives.
InOrder
inOrder
=
inOrder
(
mockEventListener
);
inOrder
.
verify
(
mockEventListener
,
never
()).
onPositionDiscontinuity
(
anyInt
());
inOrder
.
verify
(
mockEventListener
)
.
onTimelineChanged
(
...
...
@@ -452,12 +455,23 @@ public final class ExoPlayerTest {
.
onTimelineChanged
(
argThat
(
noUid
(
firstTimeline
)),
eq
(
Player
.
TIMELINE_CHANGE_REASON_SOURCE_UPDATE
));
inOrder
.
verify
(
mockEventListener
,
times
(
2
))
.
verify
(
mockEventListener
)
.
onTimelineChanged
(
argThat
(
noUid
(
placeholderTimeline
)),
eq
(
Player
.
TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED
));
inOrder
.
verify
(
mockEventListener
)
.
onPositionDiscontinuity
(
any
(),
any
(),
eq
(
Player
.
DISCONTINUITY_REASON_REMOVE
));
inOrder
.
verify
(
mockEventListener
)
.
onTimelineChanged
(
argThat
(
noUid
(
placeholderTimeline
)),
eq
(
Player
.
TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED
));
inOrder
.
verify
(
mockEventListener
)
.
onPositionDiscontinuity
(
any
(),
any
(),
eq
(
Player
.
DISCONTINUITY_REASON_REMOVE
));
inOrder
.
verify
(
mockEventListener
)
.
onTimelineChanged
(
argThat
(
noUid
(
thirdTimeline
)),
eq
(
Player
.
TIMELINE_CHANGE_REASON_SOURCE_UPDATE
));
inOrder
...
...
@@ -542,11 +556,11 @@ public final class ExoPlayerTest {
.
blockUntilEnded
(
TIMEOUT_MS
);
testRunner
.
assertPlayedPeriodIndices
(
0
,
1
,
0
,
2
,
1
,
2
);
testRunner
.
assertPositionDiscontinuityReasonsEqual
(
Player
.
DISCONTINUITY_REASON_
PERIOD
_TRANSITION
,
Player
.
DISCONTINUITY_REASON_
PERIOD
_TRANSITION
,
Player
.
DISCONTINUITY_REASON_
PERIOD
_TRANSITION
,
Player
.
DISCONTINUITY_REASON_
PERIOD
_TRANSITION
,
Player
.
DISCONTINUITY_REASON_
PERIOD
_TRANSITION
);
Player
.
DISCONTINUITY_REASON_
AUTO
_TRANSITION
,
Player
.
DISCONTINUITY_REASON_
AUTO
_TRANSITION
,
Player
.
DISCONTINUITY_REASON_
AUTO
_TRANSITION
,
Player
.
DISCONTINUITY_REASON_
AUTO
_TRANSITION
,
Player
.
DISCONTINUITY_REASON_
AUTO
_TRANSITION
);
assertThat
(
renderer
.
isEnded
).
isTrue
();
}
...
...
@@ -603,7 +617,7 @@ public final class ExoPlayerTest {
.
start
()
.
blockUntilEnded
(
TIMEOUT_MS
);
// There is still one discontinuity from content to content for the failed ad insertion.
testRunner
.
assertPositionDiscontinuityReasonsEqual
(
Player
.
DISCONTINUITY_REASON_A
D_INSER
TION
);
testRunner
.
assertPositionDiscontinuityReasonsEqual
(
Player
.
DISCONTINUITY_REASON_A
UTO_TRANSI
TION
);
}
@Test
...
...
@@ -1267,7 +1281,8 @@ public final class ExoPlayerTest {
Player
.
TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED
,
Player
.
TIMELINE_CHANGE_REASON_SOURCE_UPDATE
,
Player
.
TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED
);
testRunner
.
assertPositionDiscontinuityReasonsEqual
(
Player
.
DISCONTINUITY_REASON_SEEK
);
testRunner
.
assertPositionDiscontinuityReasonsEqual
(
Player
.
DISCONTINUITY_REASON_SEEK
,
Player
.
DISCONTINUITY_REASON_REMOVE
);
assertThat
(
currentWindowIndex
[
0
]).
isEqualTo
(
1
);
assertThat
(
currentPosition
[
0
]).
isGreaterThan
(
0
);
...
...
@@ -2616,8 +2631,7 @@ public final class ExoPlayerTest {
@Test
public
void
timelineUpdateWithNewMidrollAdCuePoint_dropsPrebufferedPeriod
()
throws
Exception
{
Timeline
timeline1
=
new
FakeTimeline
(
new
TimelineWindowDefinition
(
/* periodCount= */
1
,
/* id= */
0
));
Timeline
timeline1
=
new
FakeTimeline
(
TimelineWindowDefinition
.
createPlaceholder
(
/* tag= */
0
));
AdPlaybackState
adPlaybackStateWithMidroll
=
FakeTimeline
.
createAdPlaybackState
(
/* adsPerAdGroup= */
1
,
...
...
@@ -2653,9 +2667,10 @@ public final class ExoPlayerTest {
testRunner
.
assertTimelineChangeReasonsEqual
(
Player
.
TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED
,
Player
.
TIMELINE_CHANGE_REASON_SOURCE_UPDATE
,
Player
.
TIMELINE_CHANGE_REASON_SOURCE_UPDATE
);
testRunner
.
assertPlayedPeriodIndices
(
0
);
testRunner
.
assertPositionDiscontinuityReasonsEqual
(
Player
.
DISCONTINUITY_REASON_AUTO_TRANSITION
,
Player
.
DISCONTINUITY_REASON_AUTO_TRANSITION
);
assertThat
(
mediaSource
.
getCreatedMediaPeriods
()).
hasSize
(
4
);
assertThat
(
mediaSource
.
getCreatedMediaPeriods
().
get
(
0
).
nextAdGroupIndex
)
.
isEqualTo
(
C
.
INDEX_UNSET
);
...
...
@@ -2718,7 +2733,7 @@ public final class ExoPlayerTest {
// When the ad finishes, the player position should be at or after the requested seek position.
TestPlayerRunHelper
.
runUntilPositionDiscontinuity
(
player
,
Player
.
DISCONTINUITY_REASON_A
D_INSER
TION
);
player
,
Player
.
DISCONTINUITY_REASON_A
UTO_TRANSI
TION
);
assertThat
(
player
.
getCurrentPosition
()).
isAtLeast
(
seekPositionMs
);
}
...
...
@@ -3019,7 +3034,7 @@ public final class ExoPlayerTest {
@Override
public
void
onPositionDiscontinuity
(
@DiscontinuityReason
int
reason
)
{
if
(
reason
==
Player
.
DISCONTINUITY_REASON_
PERIOD
_TRANSITION
)
{
if
(
reason
==
Player
.
DISCONTINUITY_REASON_
AUTO
_TRANSITION
)
{
positionAtDiscontinuityMs
.
set
(
playerReference
.
get
().
getCurrentPosition
());
clockAtDiscontinuityMs
.
set
(
clock
.
elapsedRealtime
());
}
...
...
@@ -3242,7 +3257,7 @@ public final class ExoPlayerTest {
new
EventListener
()
{
@Override
public
void
onPositionDiscontinuity
(
@DiscontinuityReason
int
reason
)
{
if
(
reason
==
Player
.
DISCONTINUITY_REASON_
PERIOD
_TRANSITION
)
{
if
(
reason
==
Player
.
DISCONTINUITY_REASON_
AUTO
_TRANSITION
)
{
if
(
bufferedPositionAtFirstDiscontinuityMs
.
get
()
==
C
.
TIME_UNSET
)
{
bufferedPositionAtFirstDiscontinuityMs
.
set
(
playerReference
.
get
().
getBufferedPosition
());
...
...
@@ -3296,7 +3311,7 @@ public final class ExoPlayerTest {
new
EventListener
()
{
@Override
public
void
onPositionDiscontinuity
(
@DiscontinuityReason
int
reason
)
{
if
(
reason
==
Player
.
DISCONTINUITY_REASON_A
D_INSER
TION
)
{
if
(
reason
==
Player
.
DISCONTINUITY_REASON_A
UTO_TRANSI
TION
)
{
contentStartPositionMs
.
set
(
playerReference
.
get
().
getContentPosition
());
}
}
...
...
@@ -3349,7 +3364,7 @@ public final class ExoPlayerTest {
new
EventListener
()
{
@Override
public
void
onPositionDiscontinuity
(
@DiscontinuityReason
int
reason
)
{
if
(
reason
==
Player
.
DISCONTINUITY_REASON_A
D_INSER
TION
)
{
if
(
reason
==
Player
.
DISCONTINUITY_REASON_A
UTO_TRANSI
TION
)
{
contentStartPositionMs
.
set
(
playerReference
.
get
().
getContentPosition
());
}
}
...
...
@@ -7584,7 +7599,7 @@ public final class ExoPlayerTest {
firstMediaSource
.
setNewSourceInfo
(
timelineWithOffsets
);
// Wait until player transitions to second source (which also has non-zero offsets).
TestPlayerRunHelper
.
runUntilPositionDiscontinuity
(
player
,
Player
.
DISCONTINUITY_REASON_
PERIOD
_TRANSITION
);
player
,
Player
.
DISCONTINUITY_REASON_
AUTO
_TRANSITION
);
assertThat
(
player
.
getCurrentWindowIndex
()).
isEqualTo
(
1
);
player
.
release
();
...
...
@@ -7680,7 +7695,7 @@ public final class ExoPlayerTest {
@Override
public
void
onPositionDiscontinuity
(
int
reason
)
{
if
(
reason
==
Player
.
DISCONTINUITY_REASON_SEEK
||
reason
==
Player
.
DISCONTINUITY_REASON_
PERIOD
_TRANSITION
)
{
||
reason
==
Player
.
DISCONTINUITY_REASON_
AUTO
_TRANSITION
)
{
currentMediaItems
.
add
(
playerHolder
[
0
].
getCurrentMediaItem
());
}
}
...
...
@@ -9271,7 +9286,7 @@ public final class ExoPlayerTest {
verify
(
listener
).
onTimelineChanged
(
any
(),
anyInt
());
verify
(
listener
).
onMediaItemTransition
(
any
(),
anyInt
());
verify
(
listener
).
onPositionDiscontinuity
(
anyInt
());
verify
(
listener
).
onPositionDiscontinuity
(
any
(),
any
(),
any
Int
());
verify
(
listener
).
onPlaybackParametersChanged
(
any
());
ArgumentCaptor
<
Player
.
Events
>
eventCaptor
=
ArgumentCaptor
.
forClass
(
Player
.
Events
.
class
);
verify
(
listener
).
onEvents
(
eq
(
player
),
eventCaptor
.
capture
());
...
...
@@ -9312,7 +9327,7 @@ public final class ExoPlayerTest {
// Verify that all callbacks have been called at least once.
verify
(
listener
,
atLeastOnce
()).
onTimelineChanged
(
any
(),
anyInt
());
verify
(
listener
,
atLeastOnce
()).
onMediaItemTransition
(
any
(),
anyInt
());
verify
(
listener
,
atLeastOnce
()).
onPositionDiscontinuity
(
anyInt
());
verify
(
listener
,
atLeastOnce
()).
onPositionDiscontinuity
(
any
(),
any
(),
any
Int
());
verify
(
listener
,
atLeastOnce
()).
onPlaybackParametersChanged
(
any
());
verify
(
listener
,
atLeastOnce
()).
onRepeatModeChanged
(
anyInt
());
verify
(
listener
,
atLeastOnce
()).
onShuffleModeEnabledChanged
(
anyBoolean
());
...
...
@@ -9342,6 +9357,877 @@ public final class ExoPlayerTest {
assertThat
(
containsEvent
(
allEvents
,
Player
.
EVENT_PLAYER_ERROR
)).
isTrue
();
}
@Test
public
void
play_withPreMidAndPostRollAd_callsOnDiscontinuityCorrectly
()
throws
Exception
{
ExoPlayer
player
=
new
TestExoPlayerBuilder
(
context
).
build
();
EventListener
listener
=
mock
(
EventListener
.
class
);
player
.
addListener
(
listener
);
AdPlaybackState
adPlaybackState
=
FakeTimeline
.
createAdPlaybackState
(
/* adsPerAdGroup= */
2
,
/* adGroupTimesUs...= */
0
,
7
*
C
.
MICROS_PER_SECOND
,
C
.
TIME_END_OF_SOURCE
);
TimelineWindowDefinition
adTimeline
=
new
TimelineWindowDefinition
(
/* periodCount= */
1
,
/* id= */
0
,
/* isSeekable= */
true
,
/* isDynamic= */
false
,
/* isLive= */
false
,
/* isPlaceholder= */
false
,
/* durationUs= */
10
*
C
.
MICROS_PER_SECOND
,
/* defaultPositionUs= */
0
,
/* windowOffsetInFirstPeriodUs= */
0
,
adPlaybackState
);
player
.
setMediaSource
(
new
FakeMediaSource
(
new
FakeTimeline
(
adTimeline
)));
player
.
prepare
();
player
.
play
();
TestPlayerRunHelper
.
runUntilPlaybackState
(
player
,
Player
.
STATE_ENDED
);
ArgumentCaptor
<
Player
.
PositionInfo
>
oldPosition
=
ArgumentCaptor
.
forClass
(
Player
.
PositionInfo
.
class
);
ArgumentCaptor
<
Player
.
PositionInfo
>
newPosition
=
ArgumentCaptor
.
forClass
(
Player
.
PositionInfo
.
class
);
verify
(
listener
,
never
())
.
onPositionDiscontinuity
(
any
(),
any
(),
not
(
eq
(
Player
.
DISCONTINUITY_REASON_AUTO_TRANSITION
)));
verify
(
listener
,
times
(
8
))
.
onPositionDiscontinuity
(
oldPosition
.
capture
(),
newPosition
.
capture
(),
eq
(
Player
.
DISCONTINUITY_REASON_AUTO_TRANSITION
));
// first ad group (pre-roll)
// starts with ad to ad transition
List
<
Player
.
PositionInfo
>
oldPositions
=
oldPosition
.
getAllValues
();
List
<
Player
.
PositionInfo
>
newPositions
=
newPosition
.
getAllValues
();
assertThat
(
oldPositions
.
get
(
0
).
windowIndex
).
isEqualTo
(
0
);
assertThat
(
oldPositions
.
get
(
0
).
positionMs
).
isEqualTo
(
5000
);
assertThat
(
oldPositions
.
get
(
0
).
contentPositionMs
).
isEqualTo
(
0
);
assertThat
(
oldPositions
.
get
(
0
).
adGroupIndex
).
isEqualTo
(
0
);
assertThat
(
oldPositions
.
get
(
0
).
adIndexInAdGroup
).
isEqualTo
(
0
);
assertThat
(
newPositions
.
get
(
0
).
windowIndex
).
isEqualTo
(
0
);
assertThat
(
newPositions
.
get
(
0
).
positionMs
).
isEqualTo
(
0
);
assertThat
(
newPositions
.
get
(
0
).
contentPositionMs
).
isEqualTo
(
0
);
assertThat
(
newPositions
.
get
(
0
).
adGroupIndex
).
isEqualTo
(
0
);
assertThat
(
newPositions
.
get
(
0
).
adIndexInAdGroup
).
isEqualTo
(
1
);
// ad to content transition
assertThat
(
oldPositions
.
get
(
1
).
windowIndex
).
isEqualTo
(
0
);
assertThat
(
oldPositions
.
get
(
1
).
positionMs
).
isEqualTo
(
5000
);
assertThat
(
oldPositions
.
get
(
1
).
contentPositionMs
).
isEqualTo
(
0
);
assertThat
(
oldPositions
.
get
(
1
).
adGroupIndex
).
isEqualTo
(
0
);
assertThat
(
oldPositions
.
get
(
1
).
adIndexInAdGroup
).
isEqualTo
(
1
);
assertThat
(
newPositions
.
get
(
1
).
windowIndex
).
isEqualTo
(
0
);
assertThat
(
newPositions
.
get
(
1
).
positionMs
).
isEqualTo
(
0
);
assertThat
(
newPositions
.
get
(
1
).
contentPositionMs
).
isEqualTo
(
0
);
assertThat
(
newPositions
.
get
(
1
).
adGroupIndex
).
isEqualTo
(-
1
);
assertThat
(
newPositions
.
get
(
1
).
adIndexInAdGroup
).
isEqualTo
(-
1
);
// second add group (mid-roll)
assertThat
(
oldPositions
.
get
(
2
).
windowIndex
).
isEqualTo
(
0
);
assertThat
(
oldPositions
.
get
(
2
).
positionMs
).
isEqualTo
(
7000
);
assertThat
(
oldPositions
.
get
(
2
).
contentPositionMs
).
isEqualTo
(
7000
);
assertThat
(
oldPositions
.
get
(
2
).
adGroupIndex
).
isEqualTo
(-
1
);
assertThat
(
oldPositions
.
get
(
2
).
adIndexInAdGroup
).
isEqualTo
(-
1
);
assertThat
(
newPositions
.
get
(
2
).
windowIndex
).
isEqualTo
(
0
);
assertThat
(
newPositions
.
get
(
2
).
positionMs
).
isEqualTo
(
0
);
assertThat
(
newPositions
.
get
(
2
).
contentPositionMs
).
isEqualTo
(
7000
);
assertThat
(
newPositions
.
get
(
2
).
adGroupIndex
).
isEqualTo
(
1
);
assertThat
(
newPositions
.
get
(
2
).
adIndexInAdGroup
).
isEqualTo
(
0
);
// ad to ad transition
assertThat
(
oldPositions
.
get
(
3
).
windowIndex
).
isEqualTo
(
0
);
assertThat
(
oldPositions
.
get
(
3
).
positionMs
).
isEqualTo
(
5000
);
assertThat
(
oldPositions
.
get
(
3
).
contentPositionMs
).
isEqualTo
(
7000
);
assertThat
(
oldPositions
.
get
(
3
).
adGroupIndex
).
isEqualTo
(
1
);
assertThat
(
oldPositions
.
get
(
3
).
adIndexInAdGroup
).
isEqualTo
(
0
);
assertThat
(
newPositions
.
get
(
3
).
windowIndex
).
isEqualTo
(
0
);
assertThat
(
newPositions
.
get
(
3
).
positionMs
).
isEqualTo
(
0
);
assertThat
(
newPositions
.
get
(
3
).
contentPositionMs
).
isEqualTo
(
7000
);
assertThat
(
newPositions
.
get
(
3
).
adGroupIndex
).
isEqualTo
(
1
);
assertThat
(
newPositions
.
get
(
3
).
adIndexInAdGroup
).
isEqualTo
(
1
);
// ad to content transition
assertThat
(
oldPositions
.
get
(
4
).
windowIndex
).
isEqualTo
(
0
);
assertThat
(
oldPositions
.
get
(
4
).
positionMs
).
isEqualTo
(
5000
);
assertThat
(
oldPositions
.
get
(
4
).
contentPositionMs
).
isEqualTo
(
7000
);
assertThat
(
oldPositions
.
get
(
4
).
adGroupIndex
).
isEqualTo
(
1
);
assertThat
(
oldPositions
.
get
(
4
).
adIndexInAdGroup
).
isEqualTo
(
1
);
assertThat
(
newPositions
.
get
(
4
).
windowIndex
).
isEqualTo
(
0
);
assertThat
(
newPositions
.
get
(
4
).
positionMs
).
isEqualTo
(
7000
);
assertThat
(
newPositions
.
get
(
4
).
contentPositionMs
).
isEqualTo
(
7000
);
assertThat
(
newPositions
.
get
(
4
).
adGroupIndex
).
isEqualTo
(-
1
);
assertThat
(
newPositions
.
get
(
4
).
adIndexInAdGroup
).
isEqualTo
(-
1
);
// third add group (post-roll)
assertThat
(
oldPositions
.
get
(
5
).
windowIndex
).
isEqualTo
(
0
);
assertThat
(
oldPositions
.
get
(
5
).
positionMs
).
isEqualTo
(
10000
);
assertThat
(
oldPositions
.
get
(
5
).
contentPositionMs
).
isEqualTo
(
10000
);
assertThat
(
oldPositions
.
get
(
5
).
adGroupIndex
).
isEqualTo
(-
1
);
assertThat
(
oldPositions
.
get
(
5
).
adIndexInAdGroup
).
isEqualTo
(-
1
);
assertThat
(
newPositions
.
get
(
5
).
windowIndex
).
isEqualTo
(
0
);
assertThat
(
newPositions
.
get
(
5
).
positionMs
).
isEqualTo
(
0
);
assertThat
(
newPositions
.
get
(
5
).
contentPositionMs
).
isEqualTo
(
10000
);
assertThat
(
newPositions
.
get
(
5
).
adGroupIndex
).
isEqualTo
(
2
);
assertThat
(
newPositions
.
get
(
5
).
adIndexInAdGroup
).
isEqualTo
(
0
);
// ad to ad transition
assertThat
(
oldPositions
.
get
(
6
).
windowIndex
).
isEqualTo
(
0
);
assertThat
(
oldPositions
.
get
(
6
).
positionMs
).
isEqualTo
(
5000
);
assertThat
(
oldPositions
.
get
(
6
).
contentPositionMs
).
isEqualTo
(
10000
);
assertThat
(
oldPositions
.
get
(
6
).
adGroupIndex
).
isEqualTo
(
2
);
assertThat
(
oldPositions
.
get
(
6
).
adIndexInAdGroup
).
isEqualTo
(
0
);
assertThat
(
newPositions
.
get
(
6
).
windowIndex
).
isEqualTo
(
0
);
assertThat
(
newPositions
.
get
(
6
).
positionMs
).
isEqualTo
(
0
);
assertThat
(
newPositions
.
get
(
6
).
contentPositionMs
).
isEqualTo
(
10000
);
assertThat
(
newPositions
.
get
(
6
).
adGroupIndex
).
isEqualTo
(
2
);
assertThat
(
newPositions
.
get
(
6
).
adIndexInAdGroup
).
isEqualTo
(
1
);
// post roll ad to end of content transition
assertThat
(
oldPositions
.
get
(
7
).
windowIndex
).
isEqualTo
(
0
);
assertThat
(
oldPositions
.
get
(
7
).
positionMs
).
isEqualTo
(
5000
);
assertThat
(
oldPositions
.
get
(
7
).
contentPositionMs
).
isEqualTo
(
10000
);
assertThat
(
oldPositions
.
get
(
7
).
adGroupIndex
).
isEqualTo
(
2
);
assertThat
(
oldPositions
.
get
(
7
).
adIndexInAdGroup
).
isEqualTo
(
1
);
assertThat
(
newPositions
.
get
(
7
).
windowIndex
).
isEqualTo
(
0
);
assertThat
(
newPositions
.
get
(
7
).
positionMs
).
isEqualTo
(
9999
);
assertThat
(
newPositions
.
get
(
7
).
contentPositionMs
).
isEqualTo
(
9999
);
assertThat
(
newPositions
.
get
(
7
).
adGroupIndex
).
isEqualTo
(-
1
);
assertThat
(
newPositions
.
get
(
7
).
adIndexInAdGroup
).
isEqualTo
(-
1
);
player
.
release
();
}
@Test
public
void
seekTo_seekOverMidRoll_callsOnDiscontinuityCorrectly
()
throws
Exception
{
ExoPlayer
player
=
new
TestExoPlayerBuilder
(
context
).
build
();
EventListener
listener
=
mock
(
EventListener
.
class
);
player
.
addListener
(
listener
);
AdPlaybackState
adPlaybackState
=
FakeTimeline
.
createAdPlaybackState
(
/* adsPerAdGroup= */
1
,
/* adGroupTimesUs...= */
2
*
C
.
MICROS_PER_SECOND
);
TimelineWindowDefinition
adTimeline
=
new
TimelineWindowDefinition
(
/* periodCount= */
1
,
/* id= */
0
,
/* isSeekable= */
true
,
/* isDynamic= */
false
,
/* isLive= */
false
,
/* isPlaceholder= */
false
,
/* durationUs= */
10
*
C
.
MICROS_PER_SECOND
,
/* defaultPositionUs= */
0
,
/* windowOffsetInFirstPeriodUs= */
0
,
adPlaybackState
);
player
.
setMediaSource
(
new
FakeMediaSource
(
new
FakeTimeline
(
adTimeline
)));
player
.
prepare
();
TestPlayerRunHelper
.
playUntilPosition
(
player
,
/* windowIndex= */
0
,
/* positionMs= */
1000
);
player
.
seekTo
(
/* positionMs= */
8_000
);
player
.
play
();
TestPlayerRunHelper
.
runUntilPlaybackState
(
player
,
Player
.
STATE_ENDED
);
ArgumentCaptor
<
Player
.
PositionInfo
>
oldPosition
=
ArgumentCaptor
.
forClass
(
Player
.
PositionInfo
.
class
);
ArgumentCaptor
<
Player
.
PositionInfo
>
newPosition
=
ArgumentCaptor
.
forClass
(
Player
.
PositionInfo
.
class
);
verify
(
listener
)
.
onPositionDiscontinuity
(
oldPosition
.
capture
(),
newPosition
.
capture
(),
eq
(
Player
.
DISCONTINUITY_REASON_SEEK
));
verify
(
listener
)
.
onPositionDiscontinuity
(
oldPosition
.
capture
(),
newPosition
.
capture
(),
eq
(
Player
.
DISCONTINUITY_REASON_SEEK_ADJUSTMENT
));
verify
(
listener
)
.
onPositionDiscontinuity
(
oldPosition
.
capture
(),
newPosition
.
capture
(),
eq
(
Player
.
DISCONTINUITY_REASON_AUTO_TRANSITION
));
verify
(
listener
,
never
())
.
onPositionDiscontinuity
(
any
(),
any
(),
eq
(
Player
.
DISCONTINUITY_REASON_REMOVE
));
verify
(
listener
,
never
())
.
onPositionDiscontinuity
(
any
(),
any
(),
eq
(
Player
.
DISCONTINUITY_REASON_SKIP
));
List
<
Player
.
PositionInfo
>
oldPositions
=
oldPosition
.
getAllValues
();
List
<
Player
.
PositionInfo
>
newPositions
=
newPosition
.
getAllValues
();
// SEEK behind mid roll
assertThat
(
oldPositions
.
get
(
0
).
windowIndex
).
isEqualTo
(
0
);
assertThat
(
oldPositions
.
get
(
0
).
positionMs
).
isIn
(
Range
.
closed
(
980L
,
1_000L
));
assertThat
(
oldPositions
.
get
(
0
).
contentPositionMs
).
isIn
(
Range
.
closed
(
980L
,
1_000L
));
assertThat
(
oldPositions
.
get
(
0
).
adGroupIndex
).
isEqualTo
(-
1
);
assertThat
(
oldPositions
.
get
(
0
).
adIndexInAdGroup
).
isEqualTo
(-
1
);
assertThat
(
newPositions
.
get
(
0
).
windowIndex
).
isEqualTo
(
0
);
assertThat
(
newPositions
.
get
(
0
).
positionMs
).
isEqualTo
(
8_000
);
assertThat
(
newPositions
.
get
(
0
).
contentPositionMs
).
isEqualTo
(
8_000
);
assertThat
(
newPositions
.
get
(
0
).
adGroupIndex
).
isEqualTo
(-
1
);
assertThat
(
newPositions
.
get
(
0
).
adIndexInAdGroup
).
isEqualTo
(-
1
);
// SEEK_ADJUSTMENT back to ad
assertThat
(
oldPositions
.
get
(
1
).
windowIndex
).
isEqualTo
(
0
);
assertThat
(
oldPositions
.
get
(
1
).
positionMs
).
isEqualTo
(
8_000
);
assertThat
(
oldPositions
.
get
(
1
).
contentPositionMs
).
isEqualTo
(
8_000
);
assertThat
(
oldPositions
.
get
(
1
).
adGroupIndex
).
isEqualTo
(-
1
);
assertThat
(
oldPositions
.
get
(
1
).
adIndexInAdGroup
).
isEqualTo
(-
1
);
assertThat
(
newPositions
.
get
(
1
).
windowIndex
).
isEqualTo
(
0
);
assertThat
(
newPositions
.
get
(
1
).
positionMs
).
isEqualTo
(
0
);
assertThat
(
newPositions
.
get
(
1
).
contentPositionMs
).
isEqualTo
(
8000
);
assertThat
(
newPositions
.
get
(
1
).
adGroupIndex
).
isEqualTo
(
0
);
assertThat
(
newPositions
.
get
(
1
).
adIndexInAdGroup
).
isEqualTo
(
0
);
// AUTO_TRANSITION back to content
assertThat
(
oldPositions
.
get
(
2
).
windowIndex
).
isEqualTo
(
0
);
assertThat
(
oldPositions
.
get
(
2
).
positionMs
).
isEqualTo
(
5_000
);
assertThat
(
oldPositions
.
get
(
2
).
contentPositionMs
).
isEqualTo
(
8_000
);
assertThat
(
oldPositions
.
get
(
2
).
adGroupIndex
).
isEqualTo
(
0
);
assertThat
(
oldPositions
.
get
(
2
).
adIndexInAdGroup
).
isEqualTo
(
0
);
assertThat
(
newPositions
.
get
(
2
).
windowIndex
).
isEqualTo
(
0
);
assertThat
(
newPositions
.
get
(
2
).
positionMs
).
isEqualTo
(
8_000
);
assertThat
(
newPositions
.
get
(
2
).
contentPositionMs
).
isEqualTo
(
8_000
);
assertThat
(
newPositions
.
get
(
2
).
adGroupIndex
).
isEqualTo
(-
1
);
assertThat
(
newPositions
.
get
(
2
).
adIndexInAdGroup
).
isEqualTo
(-
1
);
player
.
release
();
}
@Test
public
void
play_multiItemPlaylistWidthAds_callsOnDiscontinuityCorrectly
()
throws
Exception
{
ExoPlayer
player
=
new
TestExoPlayerBuilder
(
context
).
build
();
EventListener
listener
=
mock
(
EventListener
.
class
);
player
.
addListener
(
listener
);
AdPlaybackState
postRollAdPlaybackState
=
FakeTimeline
.
createAdPlaybackState
(
/* adsPerAdGroup= */
1
,
/* adGroupTimesUs...= */
C
.
TIME_END_OF_SOURCE
);
TimelineWindowDefinition
postRollWindow
=
new
TimelineWindowDefinition
(
/* periodCount= */
1
,
/* id= */
0
,
/* isSeekable= */
true
,
/* isDynamic= */
false
,
/* isLive= */
false
,
/* isPlaceholder= */
false
,
/* durationUs= */
20
*
C
.
MICROS_PER_SECOND
,
/* defaultPositionUs= */
0
,
/* windowOffsetInFirstPeriodUs= */
0
,
postRollAdPlaybackState
);
AdPlaybackState
preRollAdPlaybackState
=
FakeTimeline
.
createAdPlaybackState
(
/* adsPerAdGroup= */
1
,
/* adGroupTimesUs...= */
0
);
TimelineWindowDefinition
preRollWindow
=
new
TimelineWindowDefinition
(
/* periodCount= */
1
,
/* id= */
0
,
/* isSeekable= */
true
,
/* isDynamic= */
false
,
/* isLive= */
false
,
/* isPlaceholder= */
false
,
/* durationUs= */
25
*
C
.
MICROS_PER_SECOND
,
/* defaultPositionUs= */
0
,
/* windowOffsetInFirstPeriodUs= */
0
,
preRollAdPlaybackState
);
player
.
setMediaSources
(
Lists
.
newArrayList
(
new
FakeMediaSource
(),
new
FakeMediaSource
(
new
FakeTimeline
(
new
TimelineWindowDefinition
(
/* isSeekable= */
true
,
/* isDynamic= */
false
,
/* durationUs= */
15
*
C
.
MICROS_PER_SECOND
))),
new
FakeMediaSource
(
new
FakeTimeline
(
postRollWindow
)),
new
FakeMediaSource
(
new
FakeTimeline
(
preRollWindow
))));
player
.
prepare
();
player
.
play
();
TestPlayerRunHelper
.
runUntilPlaybackState
(
player
,
Player
.
STATE_ENDED
);
ArgumentCaptor
<
Player
.
PositionInfo
>
oldPosition
=
ArgumentCaptor
.
forClass
(
Player
.
PositionInfo
.
class
);
ArgumentCaptor
<
Player
.
PositionInfo
>
newPosition
=
ArgumentCaptor
.
forClass
(
Player
.
PositionInfo
.
class
);
verify
(
listener
,
never
())
.
onPositionDiscontinuity
(
any
(),
any
(),
not
(
eq
(
Player
.
DISCONTINUITY_REASON_AUTO_TRANSITION
)));
verify
(
listener
,
times
(
6
))
.
onPositionDiscontinuity
(
oldPosition
.
capture
(),
newPosition
.
capture
(),
eq
(
Player
.
DISCONTINUITY_REASON_AUTO_TRANSITION
));
// from first to second window
List
<
Player
.
PositionInfo
>
oldPositions
=
oldPosition
.
getAllValues
();
List
<
Player
.
PositionInfo
>
newPositions
=
newPosition
.
getAllValues
();
Window
window
=
new
Window
();
assertThat
(
oldPositions
.
get
(
0
).
windowUid
)
.
isEqualTo
(
player
.
getCurrentTimeline
().
getWindow
(
0
,
window
).
uid
);
assertThat
(
newPositions
.
get
(
0
).
windowUid
)
.
isEqualTo
(
player
.
getCurrentTimeline
().
getWindow
(
1
,
window
).
uid
);
assertThat
(
oldPositions
.
get
(
0
).
windowIndex
).
isEqualTo
(
0
);
assertThat
(
oldPositions
.
get
(
0
).
positionMs
).
isEqualTo
(
10_000
);
assertThat
(
oldPositions
.
get
(
0
).
contentPositionMs
).
isEqualTo
(
10_000
);
assertThat
(
oldPositions
.
get
(
0
).
adGroupIndex
).
isEqualTo
(-
1
);
assertThat
(
oldPositions
.
get
(
0
).
adIndexInAdGroup
).
isEqualTo
(-
1
);
assertThat
(
newPositions
.
get
(
0
).
windowIndex
).
isEqualTo
(
1
);
assertThat
(
newPositions
.
get
(
0
).
positionMs
).
isEqualTo
(
0
);
assertThat
(
newPositions
.
get
(
0
).
contentPositionMs
).
isEqualTo
(
0
);
assertThat
(
newPositions
.
get
(
0
).
adGroupIndex
).
isEqualTo
(-
1
);
assertThat
(
newPositions
.
get
(
0
).
adIndexInAdGroup
).
isEqualTo
(-
1
);
// from second window to third
assertThat
(
oldPositions
.
get
(
1
).
windowUid
)
.
isEqualTo
(
player
.
getCurrentTimeline
().
getWindow
(
1
,
window
).
uid
);
assertThat
(
newPositions
.
get
(
1
).
windowUid
)
.
isEqualTo
(
player
.
getCurrentTimeline
().
getWindow
(
2
,
window
).
uid
);
assertThat
(
oldPositions
.
get
(
1
).
windowIndex
).
isEqualTo
(
1
);
assertThat
(
oldPositions
.
get
(
1
).
positionMs
).
isEqualTo
(
15_000
);
assertThat
(
oldPositions
.
get
(
1
).
contentPositionMs
).
isEqualTo
(
15_000
);
assertThat
(
oldPositions
.
get
(
1
).
adGroupIndex
).
isEqualTo
(-
1
);
assertThat
(
oldPositions
.
get
(
1
).
adIndexInAdGroup
).
isEqualTo
(-
1
);
assertThat
(
newPositions
.
get
(
1
).
windowIndex
).
isEqualTo
(
2
);
assertThat
(
newPositions
.
get
(
1
).
positionMs
).
isEqualTo
(
0
);
assertThat
(
newPositions
.
get
(
1
).
contentPositionMs
).
isEqualTo
(
0
);
assertThat
(
newPositions
.
get
(
1
).
adGroupIndex
).
isEqualTo
(-
1
);
assertThat
(
newPositions
.
get
(
1
).
adIndexInAdGroup
).
isEqualTo
(-
1
);
// from third window content to post roll ad
assertThat
(
oldPositions
.
get
(
2
).
windowIndex
).
isEqualTo
(
2
);
assertThat
(
oldPositions
.
get
(
2
).
windowUid
).
isEqualTo
(
newPositions
.
get
(
2
).
windowUid
);
assertThat
(
oldPositions
.
get
(
2
).
positionMs
).
isEqualTo
(
20_000
);
assertThat
(
oldPositions
.
get
(
2
).
contentPositionMs
).
isEqualTo
(
20_000
);
assertThat
(
oldPositions
.
get
(
2
).
adGroupIndex
).
isEqualTo
(-
1
);
assertThat
(
oldPositions
.
get
(
2
).
adIndexInAdGroup
).
isEqualTo
(-
1
);
assertThat
(
newPositions
.
get
(
2
).
windowIndex
).
isEqualTo
(
2
);
assertThat
(
newPositions
.
get
(
2
).
positionMs
).
isEqualTo
(
0
);
assertThat
(
newPositions
.
get
(
2
).
contentPositionMs
).
isEqualTo
(
20_000
);
assertThat
(
newPositions
.
get
(
2
).
adGroupIndex
).
isEqualTo
(
0
);
assertThat
(
newPositions
.
get
(
2
).
adIndexInAdGroup
).
isEqualTo
(
0
);
// from third window post roll to third window content end
assertThat
(
oldPositions
.
get
(
3
).
windowUid
).
isEqualTo
(
newPositions
.
get
(
2
).
windowUid
);
assertThat
(
oldPositions
.
get
(
3
).
windowIndex
).
isEqualTo
(
2
);
assertThat
(
oldPositions
.
get
(
3
).
positionMs
).
isEqualTo
(
5_000
);
assertThat
(
oldPositions
.
get
(
3
).
contentPositionMs
).
isEqualTo
(
20_000
);
assertThat
(
oldPositions
.
get
(
3
).
adGroupIndex
).
isEqualTo
(
0
);
assertThat
(
oldPositions
.
get
(
3
).
adIndexInAdGroup
).
isEqualTo
(
0
);
assertThat
(
newPositions
.
get
(
3
).
windowUid
).
isEqualTo
(
oldPositions
.
get
(
3
).
windowUid
);
assertThat
(
newPositions
.
get
(
3
).
windowIndex
).
isEqualTo
(
2
);
assertThat
(
newPositions
.
get
(
3
).
positionMs
).
isEqualTo
(
19_999
);
assertThat
(
newPositions
.
get
(
3
).
contentPositionMs
).
isEqualTo
(
19_999
);
assertThat
(
newPositions
.
get
(
3
).
adGroupIndex
).
isEqualTo
(-
1
);
assertThat
(
newPositions
.
get
(
3
).
adIndexInAdGroup
).
isEqualTo
(-
1
);
// from third window content end to fourth window pre roll ad
assertThat
(
oldPositions
.
get
(
4
).
windowUid
).
isEqualTo
(
newPositions
.
get
(
3
).
windowUid
);
assertThat
(
oldPositions
.
get
(
4
).
windowIndex
).
isEqualTo
(
2
);
assertThat
(
oldPositions
.
get
(
4
).
positionMs
).
isEqualTo
(
20_000
);
assertThat
(
oldPositions
.
get
(
4
).
contentPositionMs
).
isEqualTo
(
20_000
);
assertThat
(
oldPositions
.
get
(
4
).
adGroupIndex
).
isEqualTo
(-
1
);
assertThat
(
oldPositions
.
get
(
4
).
adIndexInAdGroup
).
isEqualTo
(-
1
);
assertThat
(
newPositions
.
get
(
4
).
windowUid
).
isNotEqualTo
(
oldPositions
.
get
(
4
).
windowUid
);
assertThat
(
newPositions
.
get
(
4
).
windowIndex
).
isEqualTo
(
3
);
assertThat
(
newPositions
.
get
(
4
).
positionMs
).
isEqualTo
(
0
);
assertThat
(
newPositions
.
get
(
4
).
contentPositionMs
).
isEqualTo
(
0
);
assertThat
(
newPositions
.
get
(
4
).
adGroupIndex
).
isEqualTo
(
0
);
assertThat
(
newPositions
.
get
(
4
).
adIndexInAdGroup
).
isEqualTo
(
0
);
// from fourth window pre roll ad to fourth window content
assertThat
(
oldPositions
.
get
(
5
).
windowUid
).
isEqualTo
(
newPositions
.
get
(
4
).
windowUid
);
assertThat
(
oldPositions
.
get
(
5
).
windowIndex
).
isEqualTo
(
3
);
assertThat
(
oldPositions
.
get
(
5
).
positionMs
).
isEqualTo
(
5_000
);
assertThat
(
oldPositions
.
get
(
5
).
contentPositionMs
).
isEqualTo
(
0
);
assertThat
(
oldPositions
.
get
(
5
).
adGroupIndex
).
isEqualTo
(
0
);
assertThat
(
oldPositions
.
get
(
5
).
adIndexInAdGroup
).
isEqualTo
(
0
);
assertThat
(
newPositions
.
get
(
5
).
windowUid
).
isEqualTo
(
oldPositions
.
get
(
5
).
windowUid
);
assertThat
(
newPositions
.
get
(
5
).
windowIndex
).
isEqualTo
(
3
);
assertThat
(
newPositions
.
get
(
5
).
positionMs
).
isEqualTo
(
0
);
assertThat
(
newPositions
.
get
(
5
).
contentPositionMs
).
isEqualTo
(
0
);
assertThat
(
newPositions
.
get
(
5
).
adGroupIndex
).
isEqualTo
(-
1
);
assertThat
(
newPositions
.
get
(
5
).
adIndexInAdGroup
).
isEqualTo
(-
1
);
player
.
release
();
}
@Test
public
void
setMediaSources_removesPlayingPeriod_callsOnPositionDiscontinuity
()
throws
Exception
{
FakeMediaSource
secondMediaSource
=
new
FakeMediaSource
(
new
FakeTimeline
(
new
TimelineWindowDefinition
(
/* periodCount= */
1
,
/* id= */
2
,
/* isSeekable= */
true
,
/* isDynamic= */
false
,
/* durationUs= */
15
*
C
.
MICROS_PER_SECOND
)));
ExoPlayer
player
=
new
TestExoPlayerBuilder
(
context
).
build
();
EventListener
listener
=
mock
(
EventListener
.
class
);
player
.
addListener
(
listener
);
player
.
setMediaSource
(
new
FakeMediaSource
(
new
FakeTimeline
(
new
TimelineWindowDefinition
(
/* periodCount= */
1
,
/* id= */
1
,
/* isSeekable= */
true
,
/* isDynamic= */
false
,
/* durationUs= */
10
*
C
.
MICROS_PER_SECOND
))));
player
.
prepare
();
TestPlayerRunHelper
.
playUntilPosition
(
player
,
/* windowIndex= */
0
,
/* positionMs= */
5
*
C
.
MILLIS_PER_SECOND
);
player
.
setMediaSources
(
Lists
.
newArrayList
(
secondMediaSource
,
secondMediaSource
));
player
.
play
();
TestPlayerRunHelper
.
runUntilPlaybackState
(
player
,
Player
.
STATE_ENDED
);
ArgumentCaptor
<
Player
.
PositionInfo
>
oldPosition
=
ArgumentCaptor
.
forClass
(
Player
.
PositionInfo
.
class
);
ArgumentCaptor
<
Player
.
PositionInfo
>
newPosition
=
ArgumentCaptor
.
forClass
(
Player
.
PositionInfo
.
class
);
InOrder
inOrder
=
inOrder
(
listener
);
inOrder
.
verify
(
listener
)
.
onPositionDiscontinuity
(
oldPosition
.
capture
(),
newPosition
.
capture
(),
eq
(
Player
.
DISCONTINUITY_REASON_REMOVE
));
inOrder
.
verify
(
listener
)
.
onPositionDiscontinuity
(
any
(),
any
(),
eq
(
Player
.
DISCONTINUITY_REASON_AUTO_TRANSITION
));
List
<
Player
.
PositionInfo
>
oldPositions
=
oldPosition
.
getAllValues
();
List
<
Player
.
PositionInfo
>
newPositions
=
newPosition
.
getAllValues
();
assertThat
(
oldPositions
.
get
(
0
).
windowIndex
).
isEqualTo
(
0
);
assertThat
(
oldPositions
.
get
(
0
).
positionMs
).
isIn
(
Range
.
closed
(
4980L
,
5000L
));
assertThat
(
oldPositions
.
get
(
0
).
contentPositionMs
).
isIn
(
Range
.
closed
(
4980L
,
5000L
));
assertThat
(
newPositions
.
get
(
0
).
windowIndex
).
isEqualTo
(
0
);
assertThat
(
newPositions
.
get
(
0
).
positionMs
).
isEqualTo
(
0
);
assertThat
(
newPositions
.
get
(
0
).
contentPositionMs
).
isEqualTo
(
0
);
player
.
release
();
}
@Test
public
void
removeMediaItems_removesPlayingPeriod_callsOnPositionDiscontinuity
()
throws
Exception
{
ExoPlayer
player
=
new
TestExoPlayerBuilder
(
context
).
build
();
EventListener
listener
=
mock
(
EventListener
.
class
);
player
.
addListener
(
listener
);
player
.
setMediaSources
(
Lists
.
newArrayList
(
new
FakeMediaSource
(
new
FakeTimeline
(
new
TimelineWindowDefinition
(
/* periodCount= */
1
,
/* id= */
1
,
/* isSeekable= */
true
,
/* isDynamic= */
false
,
/* durationUs= */
10
*
C
.
MICROS_PER_SECOND
))),
new
FakeMediaSource
(
new
FakeTimeline
(
new
TimelineWindowDefinition
(
/* periodCount= */
1
,
/* id= */
2
,
/* isSeekable= */
true
,
/* isDynamic= */
false
,
/* durationUs= */
8
*
C
.
MICROS_PER_SECOND
)))));
player
.
prepare
();
TestPlayerRunHelper
.
playUntilPosition
(
player
,
/* windowIndex= */
1
,
/* positionMs= */
5
*
C
.
MILLIS_PER_SECOND
);
player
.
removeMediaItem
(
/* index= */
1
);
player
.
seekTo
(
/* positionMs= */
0
);
TestPlayerRunHelper
.
playUntilPosition
(
player
,
/* windowIndex= */
0
,
/* positionMs= */
2
*
C
.
MILLIS_PER_SECOND
);
// Removing the last item resets the position to 0 with an empty timeline.
player
.
removeMediaItem
(
0
);
TestPlayerRunHelper
.
runUntilPlaybackState
(
player
,
Player
.
STATE_ENDED
);
ArgumentCaptor
<
Player
.
PositionInfo
>
oldPosition
=
ArgumentCaptor
.
forClass
(
Player
.
PositionInfo
.
class
);
ArgumentCaptor
<
Player
.
PositionInfo
>
newPosition
=
ArgumentCaptor
.
forClass
(
Player
.
PositionInfo
.
class
);
InOrder
inOrder
=
inOrder
(
listener
);
inOrder
.
verify
(
listener
)
.
onPositionDiscontinuity
(
any
(),
any
(),
eq
(
Player
.
DISCONTINUITY_REASON_AUTO_TRANSITION
));
inOrder
.
verify
(
listener
)
.
onTimelineChanged
(
any
(),
eq
(
Player
.
TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED
));
inOrder
.
verify
(
listener
)
.
onPositionDiscontinuity
(
oldPosition
.
capture
(),
newPosition
.
capture
(),
eq
(
Player
.
DISCONTINUITY_REASON_REMOVE
));
inOrder
.
verify
(
listener
)
.
onPositionDiscontinuity
(
any
(),
any
(),
eq
(
Player
.
DISCONTINUITY_REASON_SEEK
));
inOrder
.
verify
(
listener
)
.
onTimelineChanged
(
any
(),
eq
(
Player
.
TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED
));
inOrder
.
verify
(
listener
)
.
onPositionDiscontinuity
(
oldPosition
.
capture
(),
newPosition
.
capture
(),
eq
(
Player
.
DISCONTINUITY_REASON_REMOVE
));
List
<
Player
.
PositionInfo
>
oldPositions
=
oldPosition
.
getAllValues
();
List
<
Player
.
PositionInfo
>
newPositions
=
newPosition
.
getAllValues
();
assertThat
(
oldPositions
.
get
(
0
).
windowIndex
).
isEqualTo
(
1
);
assertThat
(
oldPositions
.
get
(
0
).
positionMs
).
isIn
(
Range
.
closed
(
4980L
,
5000L
));
assertThat
(
oldPositions
.
get
(
0
).
contentPositionMs
).
isIn
(
Range
.
closed
(
4980L
,
5000L
));
assertThat
(
newPositions
.
get
(
0
).
windowIndex
).
isEqualTo
(
0
);
assertThat
(
newPositions
.
get
(
0
).
positionMs
).
isEqualTo
(
0
);
assertThat
(
newPositions
.
get
(
0
).
contentPositionMs
).
isEqualTo
(
0
);
assertThat
(
oldPositions
.
get
(
1
).
windowIndex
).
isEqualTo
(
0
);
assertThat
(
oldPositions
.
get
(
1
).
positionMs
).
isIn
(
Range
.
closed
(
1980L
,
2000L
));
assertThat
(
oldPositions
.
get
(
1
).
contentPositionMs
).
isIn
(
Range
.
closed
(
1980L
,
2000L
));
assertThat
(
newPositions
.
get
(
1
).
windowUid
).
isNull
();
assertThat
(
newPositions
.
get
(
1
).
windowIndex
).
isEqualTo
(
0
);
assertThat
(
newPositions
.
get
(
1
).
positionMs
).
isEqualTo
(
0
);
assertThat
(
newPositions
.
get
(
1
).
contentPositionMs
).
isEqualTo
(
0
);
player
.
release
();
}
@Test
public
void
concatenatingMediaSourceRemoveMediaSource_removesPlayingPeriod_callsOnPositionDiscontinuity
()
throws
Exception
{
ExoPlayer
player
=
new
TestExoPlayerBuilder
(
context
).
build
();
EventListener
listener
=
mock
(
EventListener
.
class
);
player
.
addListener
(
listener
);
ConcatenatingMediaSource
concatenatingMediaSource
=
new
ConcatenatingMediaSource
(
new
FakeMediaSource
(
new
FakeTimeline
(
new
TimelineWindowDefinition
(
/* periodCount= */
1
,
/* id= */
1
,
/* isSeekable= */
true
,
/* isDynamic= */
false
,
/* durationUs= */
10
*
C
.
MICROS_PER_SECOND
))),
new
FakeMediaSource
(
new
FakeTimeline
(
new
TimelineWindowDefinition
(
/* periodCount= */
1
,
/* id= */
2
,
/* isSeekable= */
true
,
/* isDynamic= */
false
,
/* durationUs= */
8
*
C
.
MICROS_PER_SECOND
))),
new
FakeMediaSource
(
new
FakeTimeline
(
new
TimelineWindowDefinition
(
/* periodCount= */
1
,
/* id= */
2
,
/* isSeekable= */
true
,
/* isDynamic= */
false
,
/* durationUs= */
6
*
C
.
MICROS_PER_SECOND
))));
player
.
addMediaSource
(
concatenatingMediaSource
);
player
.
prepare
();
TestPlayerRunHelper
.
playUntilPosition
(
player
,
/* windowIndex= */
1
,
/* positionMs= */
5
*
C
.
MILLIS_PER_SECOND
);
concatenatingMediaSource
.
removeMediaSource
(
1
);
TestPlayerRunHelper
.
runUntilPendingCommandsAreFullyHandled
(
player
);
concatenatingMediaSource
.
removeMediaSource
(
1
);
player
.
play
();
TestPlayerRunHelper
.
runUntilPlaybackState
(
player
,
Player
.
STATE_ENDED
);
ArgumentCaptor
<
Player
.
PositionInfo
>
oldPosition
=
ArgumentCaptor
.
forClass
(
Player
.
PositionInfo
.
class
);
ArgumentCaptor
<
Player
.
PositionInfo
>
newPosition
=
ArgumentCaptor
.
forClass
(
Player
.
PositionInfo
.
class
);
InOrder
inOrder
=
inOrder
(
listener
);
inOrder
.
verify
(
listener
)
.
onPositionDiscontinuity
(
any
(),
any
(),
eq
(
Player
.
DISCONTINUITY_REASON_AUTO_TRANSITION
));
inOrder
.
verify
(
listener
,
times
(
2
))
.
onPositionDiscontinuity
(
oldPosition
.
capture
(),
newPosition
.
capture
(),
eq
(
Player
.
DISCONTINUITY_REASON_REMOVE
));
List
<
Player
.
PositionInfo
>
oldPositions
=
oldPosition
.
getAllValues
();
List
<
Player
.
PositionInfo
>
newPositions
=
newPosition
.
getAllValues
();
assertThat
(
oldPositions
.
get
(
0
).
windowIndex
).
isEqualTo
(
1
);
assertThat
(
oldPositions
.
get
(
0
).
positionMs
).
isIn
(
Range
.
closed
(
4980L
,
5000L
));
assertThat
(
oldPositions
.
get
(
0
).
contentPositionMs
).
isIn
(
Range
.
closed
(
4980L
,
5000L
));
assertThat
(
newPositions
.
get
(
0
).
windowIndex
).
isEqualTo
(
1
);
assertThat
(
newPositions
.
get
(
0
).
positionMs
).
isEqualTo
(
0
);
assertThat
(
newPositions
.
get
(
0
).
contentPositionMs
).
isEqualTo
(
0
);
assertThat
(
oldPositions
.
get
(
1
).
windowIndex
).
isEqualTo
(
1
);
assertThat
(
oldPositions
.
get
(
1
).
positionMs
).
isEqualTo
(
0
);
assertThat
(
oldPositions
.
get
(
1
).
contentPositionMs
).
isEqualTo
(
0
);
assertThat
(
newPositions
.
get
(
1
).
windowIndex
).
isEqualTo
(
0
);
assertThat
(
newPositions
.
get
(
1
).
positionMs
).
isEqualTo
(
0
);
assertThat
(
newPositions
.
get
(
1
).
contentPositionMs
).
isEqualTo
(
0
);
player
.
release
();
}
@Test
public
void
concatenatingMediaSourceRemoveMediaSourceWithSeek_overridesRemoval_callsOnPositionDiscontinuity
()
throws
Exception
{
ExoPlayer
player
=
new
TestExoPlayerBuilder
(
context
).
build
();
EventListener
listener
=
mock
(
EventListener
.
class
);
player
.
addListener
(
listener
);
ConcatenatingMediaSource
concatenatingMediaSource
=
new
ConcatenatingMediaSource
(
new
FakeMediaSource
(
new
FakeTimeline
(
new
TimelineWindowDefinition
(
/* periodCount= */
1
,
/* id= */
1
,
/* isSeekable= */
true
,
/* isDynamic= */
false
,
/* durationUs= */
10
*
C
.
MICROS_PER_SECOND
))),
new
FakeMediaSource
(
new
FakeTimeline
(
new
TimelineWindowDefinition
(
/* periodCount= */
1
,
/* id= */
2
,
/* isSeekable= */
true
,
/* isDynamic= */
false
,
/* durationUs= */
8
*
C
.
MICROS_PER_SECOND
))),
new
FakeMediaSource
(
new
FakeTimeline
(
new
TimelineWindowDefinition
(
/* periodCount= */
1
,
/* id= */
2
,
/* isSeekable= */
true
,
/* isDynamic= */
false
,
/* durationUs= */
6
*
C
.
MICROS_PER_SECOND
))));
player
.
addMediaSource
(
concatenatingMediaSource
);
player
.
prepare
();
TestPlayerRunHelper
.
playUntilPosition
(
player
,
/* windowIndex= */
1
,
/* positionMs= */
5
*
C
.
MILLIS_PER_SECOND
);
concatenatingMediaSource
.
removeMediaSource
(
1
);
player
.
seekTo
(
/* windowIndex= */
0
,
/* positionMs= */
1234
);
TestPlayerRunHelper
.
runUntilPendingCommandsAreFullyHandled
(
player
);
concatenatingMediaSource
.
removeMediaSource
(
0
);
player
.
play
();
TestPlayerRunHelper
.
runUntilPlaybackState
(
player
,
Player
.
STATE_ENDED
);
ArgumentCaptor
<
Player
.
PositionInfo
>
oldPosition
=
ArgumentCaptor
.
forClass
(
Player
.
PositionInfo
.
class
);
ArgumentCaptor
<
Player
.
PositionInfo
>
newPosition
=
ArgumentCaptor
.
forClass
(
Player
.
PositionInfo
.
class
);
InOrder
inOrder
=
inOrder
(
listener
);
inOrder
.
verify
(
listener
)
.
onPositionDiscontinuity
(
any
(),
any
(),
eq
(
Player
.
DISCONTINUITY_REASON_AUTO_TRANSITION
));
// SEEK overrides concatenating media source modification.
inOrder
.
verify
(
listener
)
.
onPositionDiscontinuity
(
oldPosition
.
capture
(),
newPosition
.
capture
(),
eq
(
Player
.
DISCONTINUITY_REASON_SEEK
));
inOrder
.
verify
(
listener
)
.
onPositionDiscontinuity
(
oldPosition
.
capture
(),
newPosition
.
capture
(),
eq
(
Player
.
DISCONTINUITY_REASON_REMOVE
));
// This fails once out of a hundred test runs due to a race condition whether the seek or the
// removal arrives first in EPI.
// inOrder.verify(listener, never()).onPositionDiscontinuity(any(), any(), anyInt());
List
<
Player
.
PositionInfo
>
oldPositions
=
oldPosition
.
getAllValues
();
List
<
Player
.
PositionInfo
>
newPositions
=
newPosition
.
getAllValues
();
assertThat
(
oldPositions
.
get
(
0
).
windowIndex
).
isEqualTo
(
1
);
assertThat
(
oldPositions
.
get
(
0
).
positionMs
).
isIn
(
Range
.
closed
(
4980L
,
5000L
));
assertThat
(
oldPositions
.
get
(
0
).
contentPositionMs
).
isIn
(
Range
.
closed
(
4980L
,
5000L
));
assertThat
(
newPositions
.
get
(
0
).
windowIndex
).
isEqualTo
(
0
);
assertThat
(
newPositions
.
get
(
0
).
positionMs
).
isEqualTo
(
1234
);
assertThat
(
newPositions
.
get
(
0
).
contentPositionMs
).
isEqualTo
(
1234
);
assertThat
(
oldPositions
.
get
(
1
).
windowIndex
).
isEqualTo
(
0
);
assertThat
(
oldPositions
.
get
(
1
).
positionMs
).
isEqualTo
(
1234
);
assertThat
(
oldPositions
.
get
(
1
).
contentPositionMs
).
isEqualTo
(
1234
);
assertThat
(
newPositions
.
get
(
1
).
windowIndex
).
isEqualTo
(
0
);
assertThat
(
newPositions
.
get
(
1
).
positionMs
).
isEqualTo
(
1234
);
assertThat
(
newPositions
.
get
(
1
).
contentPositionMs
).
isEqualTo
(
1234
);
player
.
release
();
}
@Test
public
void
seekTo_callsOnPositionDiscontinuity
()
throws
Exception
{
ExoPlayer
player
=
new
TestExoPlayerBuilder
(
context
).
build
();
EventListener
listener
=
mock
(
EventListener
.
class
);
player
.
addListener
(
listener
);
player
.
setMediaSources
(
Lists
.
newArrayList
(
new
FakeMediaSource
(),
new
FakeMediaSource
()));
player
.
prepare
();
TestPlayerRunHelper
.
playUntilPosition
(
player
,
/* windowIndex= */
0
,
/* positionMs= */
5
*
C
.
MILLIS_PER_SECOND
);
player
.
seekTo
(
/* positionMs= */
7
*
C
.
MILLIS_PER_SECOND
);
player
.
seekTo
(
/* windowIndex= */
1
,
/* positionMs= */
C
.
MILLIS_PER_SECOND
);
player
.
play
();
TestPlayerRunHelper
.
runUntilPlaybackState
(
player
,
Player
.
STATE_ENDED
);
ArgumentCaptor
<
Player
.
PositionInfo
>
oldPosition
=
ArgumentCaptor
.
forClass
(
Player
.
PositionInfo
.
class
);
ArgumentCaptor
<
Player
.
PositionInfo
>
newPosition
=
ArgumentCaptor
.
forClass
(
Player
.
PositionInfo
.
class
);
verify
(
listener
,
never
())
.
onPositionDiscontinuity
(
any
(),
any
(),
not
(
eq
(
Player
.
DISCONTINUITY_REASON_SEEK
)));
verify
(
listener
,
times
(
2
))
.
onPositionDiscontinuity
(
oldPosition
.
capture
(),
newPosition
.
capture
(),
eq
(
Player
.
DISCONTINUITY_REASON_SEEK
));
List
<
Player
.
PositionInfo
>
oldPositions
=
oldPosition
.
getAllValues
();
List
<
Player
.
PositionInfo
>
newPositions
=
newPosition
.
getAllValues
();
assertThat
(
oldPositions
.
get
(
0
).
windowUid
).
isEqualTo
(
newPositions
.
get
(
0
).
windowUid
);
assertThat
(
newPositions
.
get
(
0
).
windowIndex
).
isEqualTo
(
0
);
assertThat
(
oldPositions
.
get
(
0
).
positionMs
).
isIn
(
Range
.
closed
(
4980L
,
5000L
));
assertThat
(
oldPositions
.
get
(
0
).
contentPositionMs
).
isIn
(
Range
.
closed
(
4980L
,
5000L
));
assertThat
(
oldPositions
.
get
(
0
).
windowIndex
).
isEqualTo
(
0
);
assertThat
(
newPositions
.
get
(
0
).
positionMs
).
isEqualTo
(
7_000
);
assertThat
(
newPositions
.
get
(
0
).
contentPositionMs
).
isEqualTo
(
7_000
);
assertThat
(
oldPositions
.
get
(
1
).
windowIndex
).
isEqualTo
(
0
);
assertThat
(
oldPositions
.
get
(
1
).
windowUid
).
isNotEqualTo
(
newPositions
.
get
(
1
).
windowUid
);
assertThat
(
oldPositions
.
get
(
1
).
positionMs
).
isEqualTo
(
7_000
);
assertThat
(
oldPositions
.
get
(
1
).
contentPositionMs
).
isEqualTo
(
7_000
);
assertThat
(
newPositions
.
get
(
1
).
windowIndex
).
isEqualTo
(
1
);
assertThat
(
newPositions
.
get
(
1
).
positionMs
).
isEqualTo
(
1_000
);
assertThat
(
newPositions
.
get
(
1
).
contentPositionMs
).
isEqualTo
(
1_000
);
player
.
release
();
}
@Test
public
void
seekTo_whenTimelineEmpty_callsOnPositionDiscontinuity
()
{
ExoPlayer
player
=
new
TestExoPlayerBuilder
(
context
).
build
();
EventListener
listener
=
mock
(
EventListener
.
class
);
player
.
addListener
(
listener
);
player
.
seekTo
(
/* positionMs= */
7
*
C
.
MILLIS_PER_SECOND
);
player
.
seekTo
(
/* windowIndex= */
1
,
/* positionMs= */
C
.
MILLIS_PER_SECOND
);
player
.
seekTo
(
/* positionMs= */
5
*
C
.
MILLIS_PER_SECOND
);
ArgumentCaptor
<
Player
.
PositionInfo
>
oldPosition
=
ArgumentCaptor
.
forClass
(
Player
.
PositionInfo
.
class
);
ArgumentCaptor
<
Player
.
PositionInfo
>
newPosition
=
ArgumentCaptor
.
forClass
(
Player
.
PositionInfo
.
class
);
verify
(
listener
,
never
())
.
onPositionDiscontinuity
(
any
(),
any
(),
not
(
eq
(
Player
.
DISCONTINUITY_REASON_SEEK
)));
verify
(
listener
,
times
(
3
))
.
onPositionDiscontinuity
(
oldPosition
.
capture
(),
newPosition
.
capture
(),
eq
(
Player
.
DISCONTINUITY_REASON_SEEK
));
List
<
Player
.
PositionInfo
>
oldPositions
=
oldPosition
.
getAllValues
();
List
<
Player
.
PositionInfo
>
newPositions
=
newPosition
.
getAllValues
();
// a seek from initial state to masked seek position
assertThat
(
oldPositions
.
get
(
0
).
windowUid
).
isNull
();
assertThat
(
oldPositions
.
get
(
0
).
windowIndex
).
isEqualTo
(
0
);
assertThat
(
oldPositions
.
get
(
0
).
positionMs
).
isEqualTo
(
0
);
assertThat
(
oldPositions
.
get
(
0
).
contentPositionMs
).
isEqualTo
(
0
);
assertThat
(
newPositions
.
get
(
0
).
windowIndex
).
isEqualTo
(
0
);
assertThat
(
newPositions
.
get
(
0
).
windowUid
).
isNull
();
assertThat
(
newPositions
.
get
(
0
).
positionMs
).
isEqualTo
(
7_000
);
assertThat
(
newPositions
.
get
(
0
).
contentPositionMs
).
isEqualTo
(
7_000
);
// a seek from masked seek position to another masked position across windows
assertThat
(
oldPositions
.
get
(
1
).
windowUid
).
isNull
();
assertThat
(
oldPositions
.
get
(
1
).
windowIndex
).
isEqualTo
(
0
);
assertThat
(
oldPositions
.
get
(
1
).
positionMs
).
isEqualTo
(
7_000
);
assertThat
(
oldPositions
.
get
(
1
).
contentPositionMs
).
isEqualTo
(
7_000
);
assertThat
(
newPositions
.
get
(
1
).
windowUid
).
isNull
();
assertThat
(
newPositions
.
get
(
1
).
windowIndex
).
isEqualTo
(
1
);
assertThat
(
newPositions
.
get
(
1
).
positionMs
).
isEqualTo
(
1_000
);
assertThat
(
newPositions
.
get
(
1
).
contentPositionMs
).
isEqualTo
(
1_000
);
// a seek from masked seek position to another masked position within window
assertThat
(
oldPositions
.
get
(
2
).
windowUid
).
isNull
();
assertThat
(
oldPositions
.
get
(
2
).
windowIndex
).
isEqualTo
(
1
);
assertThat
(
oldPositions
.
get
(
2
).
positionMs
).
isEqualTo
(
1_000
);
assertThat
(
oldPositions
.
get
(
2
).
contentPositionMs
).
isEqualTo
(
1_000
);
assertThat
(
newPositions
.
get
(
2
).
windowUid
).
isNull
();
assertThat
(
newPositions
.
get
(
2
).
windowIndex
).
isEqualTo
(
1
);
assertThat
(
newPositions
.
get
(
2
).
positionMs
).
isEqualTo
(
5_000
);
assertThat
(
newPositions
.
get
(
2
).
contentPositionMs
).
isEqualTo
(
5_000
);
player
.
release
();
}
@Test
public
void
stop_doesNotCallOnPositionDiscontinuity
()
throws
Exception
{
ExoPlayer
player
=
new
TestExoPlayerBuilder
(
context
).
build
();
EventListener
listener
=
mock
(
EventListener
.
class
);
player
.
addListener
(
listener
);
player
.
setMediaSource
(
new
FakeMediaSource
());
player
.
prepare
();
TestPlayerRunHelper
.
playUntilPosition
(
player
,
/* windowIndex= */
0
,
/* positionMs= */
5
*
C
.
MILLIS_PER_SECOND
);
player
.
stop
();
verify
(
listener
,
never
()).
onPositionDiscontinuity
(
any
(),
any
(),
anyInt
());
player
.
release
();
}
// Tests deprecated stop(boolean reset)
@SuppressWarnings
(
"deprecation"
)
@Test
public
void
stop_withResetRemovesPlayingPeriod_callsOnPositionDiscontinuity
()
throws
Exception
{
ExoPlayer
player
=
new
TestExoPlayerBuilder
(
context
).
build
();
EventListener
listener
=
mock
(
EventListener
.
class
);
player
.
addListener
(
listener
);
player
.
setMediaSource
(
new
FakeMediaSource
());
player
.
prepare
();
TestPlayerRunHelper
.
playUntilPosition
(
player
,
/* windowIndex= */
0
,
/* positionMs= */
5
*
C
.
MILLIS_PER_SECOND
);
player
.
stop
(
/* reset= */
true
);
ArgumentCaptor
<
Player
.
PositionInfo
>
oldPosition
=
ArgumentCaptor
.
forClass
(
Player
.
PositionInfo
.
class
);
ArgumentCaptor
<
Player
.
PositionInfo
>
newPosition
=
ArgumentCaptor
.
forClass
(
Player
.
PositionInfo
.
class
);
verify
(
listener
,
never
())
.
onPositionDiscontinuity
(
any
(),
any
(),
not
(
eq
(
Player
.
DISCONTINUITY_REASON_REMOVE
)));
verify
(
listener
)
.
onPositionDiscontinuity
(
oldPosition
.
capture
(),
newPosition
.
capture
(),
eq
(
Player
.
DISCONTINUITY_REASON_REMOVE
));
List
<
Player
.
PositionInfo
>
oldPositions
=
oldPosition
.
getAllValues
();
List
<
Player
.
PositionInfo
>
newPositions
=
newPosition
.
getAllValues
();
assertThat
(
oldPositions
.
get
(
0
).
windowIndex
).
isEqualTo
(
0
);
assertThat
(
oldPositions
.
get
(
0
).
positionMs
).
isIn
(
Range
.
closed
(
4980L
,
5000L
));
assertThat
(
oldPositions
.
get
(
0
).
contentPositionMs
).
isIn
(
Range
.
closed
(
4980L
,
5000L
));
assertThat
(
newPositions
.
get
(
0
).
windowUid
).
isNull
();
assertThat
(
newPositions
.
get
(
0
).
windowIndex
).
isEqualTo
(
0
);
assertThat
(
newPositions
.
get
(
0
).
positionMs
).
isEqualTo
(
0
);
assertThat
(
newPositions
.
get
(
0
).
contentPositionMs
).
isEqualTo
(
0
);
player
.
release
();
}
@Test
public
void
seekTo_cancelsSourceDiscontinuity_callsOnPositionDiscontinuity
()
throws
Exception
{
Timeline
timeline1
=
new
FakeTimeline
(
new
TimelineWindowDefinition
(
/* periodCount= */
1
,
/* id= */
1
),
new
TimelineWindowDefinition
(
/* periodCount= */
1
,
/* id= */
2
));
final
Timeline
timeline2
=
new
FakeTimeline
(
new
TimelineWindowDefinition
(
/* periodCount= */
1
,
/* id= */
1
),
new
TimelineWindowDefinition
(
/* periodCount= */
1
,
/* id= */
3
));
final
FakeMediaSource
mediaSource
=
new
FakeMediaSource
(
timeline1
,
ExoPlayerTestRunner
.
VIDEO_FORMAT
);
ExoPlayer
player
=
new
TestExoPlayerBuilder
(
context
).
build
();
EventListener
listener
=
mock
(
EventListener
.
class
);
player
.
addListener
(
listener
);
player
.
setMediaSource
(
mediaSource
);
player
.
prepare
();
TestPlayerRunHelper
.
playUntilPosition
(
player
,
/* windowIndex= */
1
,
/* positionMs= */
2000
);
player
.
seekTo
(
/* windowIndex= */
1
,
/* positionMs= */
2122
);
// This causes a DISCONTINUITY_REASON_REMOVE between pending operations that needs to be
// cancelled by the seek below.
mediaSource
.
setNewSourceInfo
(
timeline2
);
player
.
play
();
player
.
seekTo
(
/* windowIndex= */
0
,
/* positionMs= */
2222
);
TestPlayerRunHelper
.
runUntilPlaybackState
(
player
,
Player
.
STATE_ENDED
);
ArgumentCaptor
<
Player
.
PositionInfo
>
oldPosition
=
ArgumentCaptor
.
forClass
(
Player
.
PositionInfo
.
class
);
ArgumentCaptor
<
Player
.
PositionInfo
>
newPosition
=
ArgumentCaptor
.
forClass
(
Player
.
PositionInfo
.
class
);
InOrder
inOrder
=
inOrder
(
listener
);
inOrder
.
verify
(
listener
)
.
onPositionDiscontinuity
(
any
(),
any
(),
eq
(
Player
.
DISCONTINUITY_REASON_AUTO_TRANSITION
));
inOrder
.
verify
(
listener
,
times
(
2
))
.
onPositionDiscontinuity
(
oldPosition
.
capture
(),
newPosition
.
capture
(),
eq
(
Player
.
DISCONTINUITY_REASON_SEEK
));
inOrder
.
verify
(
listener
)
.
onPositionDiscontinuity
(
any
(),
any
(),
eq
(
Player
.
DISCONTINUITY_REASON_AUTO_TRANSITION
));
inOrder
.
verify
(
listener
,
never
()).
onPositionDiscontinuity
(
any
(),
any
(),
anyInt
());
List
<
Player
.
PositionInfo
>
oldPositions
=
oldPosition
.
getAllValues
();
List
<
Player
.
PositionInfo
>
newPositions
=
newPosition
.
getAllValues
();
// First seek
assertThat
(
oldPositions
.
get
(
0
).
windowIndex
).
isEqualTo
(
1
);
assertThat
(
oldPositions
.
get
(
0
).
positionMs
).
isIn
(
Range
.
closed
(
1980L
,
2000L
));
assertThat
(
oldPositions
.
get
(
0
).
contentPositionMs
).
isIn
(
Range
.
closed
(
1980L
,
2000L
));
assertThat
(
newPositions
.
get
(
0
).
windowIndex
).
isEqualTo
(
1
);
assertThat
(
newPositions
.
get
(
0
).
positionMs
).
isEqualTo
(
2122
);
assertThat
(
newPositions
.
get
(
0
).
contentPositionMs
).
isEqualTo
(
2122
);
// Second seek.
assertThat
(
oldPositions
.
get
(
1
).
windowIndex
).
isEqualTo
(
1
);
assertThat
(
oldPositions
.
get
(
1
).
positionMs
).
isEqualTo
(
2122
);
assertThat
(
oldPositions
.
get
(
1
).
contentPositionMs
).
isEqualTo
(
2122
);
assertThat
(
newPositions
.
get
(
1
).
windowIndex
).
isEqualTo
(
0
);
assertThat
(
newPositions
.
get
(
1
).
positionMs
).
isEqualTo
(
2222
);
assertThat
(
newPositions
.
get
(
1
).
contentPositionMs
).
isEqualTo
(
2222
);
player
.
release
();
}
// Internal methods.
private
static
ActionSchedule
.
Builder
addSurfaceSwitch
(
ActionSchedule
.
Builder
builder
)
{
...
...
library/core/src/test/java/com/google/android/exoplayer2/MediaPeriodQueueTest.java
View file @
dc4148d5
...
...
@@ -430,6 +430,7 @@ public final class MediaPeriodQueueTest {
mediaPeriodQueue
.
resolveMediaPeriodIdForAds
(
playlistTimeline
,
firstPeriodUid
,
/* positionUs= */
0
),
/* requestedContentPositionUs= */
C
.
TIME_UNSET
,
/* discontinuityStartPositionUs= */
0
,
Player
.
STATE_READY
,
/* playbackError= */
null
,
/* isLoading= */
false
,
...
...
library/core/src/test/java/com/google/android/exoplayer2/analytics/AnalyticsCollectorTest.java
View file @
dc4148d5
...
...
@@ -662,6 +662,8 @@ public final class AnalyticsCollectorTest {
period0Seq0
/* SOURCE_UPDATE */
,
WINDOW_0
/* PLAYLIST_CHANGE */
,
period0Seq1
/* SOURCE_UPDATE */
);
assertThat
(
listener
.
getEvents
(
EVENT_POSITION_DISCONTINUITY
))
.
containsExactly
(
WINDOW_0
/* REMOVE */
);
assertThat
(
listener
.
getEvents
(
EVENT_IS_LOADING_CHANGED
))
.
containsExactly
(
period0Seq0
,
period0Seq0
,
period0Seq1
,
period0Seq1
)
.
inOrder
();
...
...
@@ -937,6 +939,9 @@ public final class AnalyticsCollectorTest {
period0Seq0
/* SOURCE_UPDATE (second item) */
,
period0Seq1
/* PLAYLIST_CHANGED (remove) */
)
.
inOrder
();
assertThat
(
listener
.
getEvents
(
EVENT_POSITION_DISCONTINUITY
))
.
containsExactly
(
period0Seq1
/* REMOVE */
)
.
inOrder
();
assertThat
(
listener
.
getEvents
(
EVENT_IS_LOADING_CHANGED
))
.
containsExactly
(
period0Seq0
,
period0Seq0
,
period0Seq0
,
period0Seq0
);
assertThat
(
listener
.
getEvents
(
EVENT_TRACKS_CHANGED
))
...
...
@@ -1037,9 +1042,11 @@ public final class AnalyticsCollectorTest {
new
Player
.
EventListener
()
{
@Override
public
void
onPositionDiscontinuity
(
Player
.
PositionInfo
oldPosition
,
Player
.
PositionInfo
newPosition
,
@Player
.
DiscontinuityReason
int
reason
)
{
if
(!
player
.
isPlayingAd
()
&&
reason
==
Player
.
DISCONTINUITY_REASON_A
D_INSER
TION
)
{
&&
reason
==
Player
.
DISCONTINUITY_REASON_A
UTO_TRANSI
TION
)
{
// Finished playing ad. Marked as played.
adPlaybackState
.
set
(
adPlaybackState
...
...
@@ -1651,7 +1658,7 @@ public final class AnalyticsCollectorTest {
player
.
addMediaSource
(
new
FakeMediaSource
(
new
FakeTimeline
(),
formats
));
player
.
play
();
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"
));
TestPlayerRunHelper
.
runUntilPlaybackState
(
player
,
Player
.
STATE_IDLE
);
ShadowLooper
.
runMainLooperToNextTask
();
...
...
@@ -2085,7 +2092,11 @@ public final class AnalyticsCollectorTest {
}
@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
));
}
...
...
library/core/src/test/java/com/google/android/exoplayer2/analytics/DefaultPlaybackSessionManagerTest.java
View file @
dc4148d5
...
...
@@ -461,9 +461,9 @@ public final class DefaultPlaybackSessionManagerTest {
sessionManager
.
updateSessionsWithTimelineChange
(
contentEventTime1
);
sessionManager
.
updateSessions
(
adEventTime1
);
sessionManager
.
updateSessionsWithDiscontinuity
(
adEventTime1
,
Player
.
DISCONTINUITY_REASON_A
D_INSER
TION
);
adEventTime1
,
Player
.
DISCONTINUITY_REASON_A
UTO_TRANSI
TION
);
sessionManager
.
updateSessionsWithDiscontinuity
(
contentEventTime2
,
Player
.
DISCONTINUITY_REASON_A
D_INSER
TION
);
contentEventTime2
,
Player
.
DISCONTINUITY_REASON_A
UTO_TRANSI
TION
);
String
adSessionId2
=
sessionManager
.
getSessionForMediaPeriodId
(
adTimeline
,
adEventTime2
.
mediaPeriodId
);
...
...
@@ -751,7 +751,7 @@ public final class DefaultPlaybackSessionManagerTest {
sessionManager
.
updateSessions
(
eventTime2
);
sessionManager
.
updateSessionsWithDiscontinuity
(
eventTime2
,
Player
.
DISCONTINUITY_REASON_
PERIOD
_TRANSITION
);
eventTime2
,
Player
.
DISCONTINUITY_REASON_
AUTO
_TRANSITION
);
verify
(
mockListener
).
onSessionCreated
(
eq
(
eventTime1
),
anyString
());
verify
(
mockListener
).
onSessionActive
(
eq
(
eventTime1
),
anyString
());
...
...
@@ -781,7 +781,7 @@ public final class DefaultPlaybackSessionManagerTest {
sessionManager
.
getSessionForMediaPeriodId
(
timeline
,
eventTime2
.
mediaPeriodId
);
sessionManager
.
updateSessionsWithDiscontinuity
(
eventTime2
,
Player
.
DISCONTINUITY_REASON_
PERIOD
_TRANSITION
);
eventTime2
,
Player
.
DISCONTINUITY_REASON_
AUTO
_TRANSITION
);
verify
(
mockListener
).
onSessionCreated
(
eventTime1
,
sessionId1
);
verify
(
mockListener
).
onSessionActive
(
eventTime1
,
sessionId1
);
...
...
@@ -960,7 +960,7 @@ public final class DefaultPlaybackSessionManagerTest {
adTimeline
,
contentEventTimeDuringPreroll
.
mediaPeriodId
);
sessionManager
.
updateSessionsWithDiscontinuity
(
contentEventTimeBetweenAds
,
Player
.
DISCONTINUITY_REASON_A
D_INSER
TION
);
contentEventTimeBetweenAds
,
Player
.
DISCONTINUITY_REASON_A
UTO_TRANSI
TION
);
InOrder
inOrder
=
inOrder
(
mockListener
);
inOrder
.
verify
(
mockListener
).
onSessionCreated
(
contentEventTimeDuringPreroll
,
contentSessionId
);
...
...
@@ -1025,7 +1025,7 @@ public final class DefaultPlaybackSessionManagerTest {
sessionManager
.
updateSessions
(
adEventTime2
);
sessionManager
.
updateSessionsWithDiscontinuity
(
adEventTime1
,
Player
.
DISCONTINUITY_REASON_A
D_INSER
TION
);
adEventTime1
,
Player
.
DISCONTINUITY_REASON_A
UTO_TRANSI
TION
);
verify
(
mockListener
,
never
()).
onSessionFinished
(
any
(),
anyString
(),
anyBoolean
());
}
...
...
@@ -1083,7 +1083,7 @@ public final class DefaultPlaybackSessionManagerTest {
sessionManager
.
getSessionForMediaPeriodId
(
adTimeline
,
adEventTime2
.
mediaPeriodId
);
sessionManager
.
updateSessionsWithDiscontinuity
(
adEventTime1
,
Player
.
DISCONTINUITY_REASON_A
D_INSER
TION
);
adEventTime1
,
Player
.
DISCONTINUITY_REASON_A
UTO_TRANSI
TION
);
sessionManager
.
updateSessionsWithDiscontinuity
(
adEventTime2
,
Player
.
DISCONTINUITY_REASON_SEEK
);
verify
(
mockListener
).
onSessionCreated
(
eq
(
contentEventTime
),
anyString
());
...
...
library/ui/src/main/java/com/google/android/exoplayer2/ui/PlayerView.java
View file @
dc4148d5
...
...
@@ -1625,7 +1625,10 @@ public class PlayerView extends FrameLayout implements AdsLoader.AdViewProvider
}
@Override
public
void
onPositionDiscontinuity
(
@DiscontinuityReason
int
reason
)
{
public
void
onPositionDiscontinuity
(
Player
.
PositionInfo
oldPosition
,
Player
.
PositionInfo
newPosition
,
@DiscontinuityReason
int
reason
)
{
if
(
isPlayingAd
()
&&
controllerHideDuringAds
)
{
hideController
();
}
...
...
library/ui/src/main/java/com/google/android/exoplayer2/ui/StyledPlayerView.java
View file @
dc4148d5
...
...
@@ -1647,7 +1647,10 @@ public class StyledPlayerView extends FrameLayout implements AdsLoader.AdViewPro
}
@Override
public
void
onPositionDiscontinuity
(
@DiscontinuityReason
int
reason
)
{
public
void
onPositionDiscontinuity
(
Player
.
PositionInfo
oldPosition
,
Player
.
PositionInfo
newPosition
,
@DiscontinuityReason
int
reason
)
{
if
(
isPlayingAd
()
&&
controllerHideDuringAds
)
{
hideController
();
}
...
...
robolectricutils/src/main/java/com/google/android/exoplayer2/robolectric/TestPlayerRunHelper.java
View file @
dc4148d5
...
...
@@ -158,8 +158,8 @@ public class TestPlayerRunHelper {
/**
* Runs tasks of the main {@link Looper} until a {@link
* Player.EventListener#onPositionDiscontinuity
} callback with the specified {@link
* Player.DiscontinuityReason} occurred.
* Player.EventListener#onPositionDiscontinuity
(Player.PositionInfo, Player.PositionInfo, int)}
*
callback with the specified {@link
Player.DiscontinuityReason} occurred.
*
* @param player The {@link Player}.
* @param expectedReason The expected {@link Player.DiscontinuityReason}.
...
...
@@ -173,7 +173,8 @@ public class TestPlayerRunHelper {
Player
.
EventListener
listener
=
new
Player
.
EventListener
()
{
@Override
public
void
onPositionDiscontinuity
(
int
reason
)
{
public
void
onPositionDiscontinuity
(
Player
.
PositionInfo
oldPosition
,
Player
.
PositionInfo
newPosition
,
int
reason
)
{
if
(
reason
==
expectedReason
)
{
receivedCallback
.
set
(
true
);
player
.
removeListener
(
this
);
...
...
testutils/src/main/java/com/google/android/exoplayer2/testutil/Action.java
View file @
dc4148d5
...
...
@@ -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
{
/** @param tag A tag to use for logging. */
...
...
@@ -824,7 +827,10 @@ public abstract class Action {
player
.
addListener
(
new
Player
.
EventListener
()
{
@Override
public
void
onPositionDiscontinuity
(
@Player
.
DiscontinuityReason
int
reason
)
{
public
void
onPositionDiscontinuity
(
Player
.
PositionInfo
oldPosition
,
Player
.
PositionInfo
newPosition
,
@Player
.
DiscontinuityReason
int
reason
)
{
player
.
removeListener
(
this
);
nextAction
.
schedule
(
player
,
trackSelector
,
surface
,
handler
);
}
...
...
testutils/src/main/java/com/google/android/exoplayer2/testutil/ExoPlayerTestRunner.java
View file @
dc4148d5
...
...
@@ -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
()
{
assertThat
(
discontinuityReasons
).
isEmpty
();
...
...
@@ -583,7 +584,8 @@ public final class ExoPlayerTestRunner implements Player.EventListener, ActionSc
/**
* 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.
*/
...
...
@@ -676,10 +678,15 @@ public final class ExoPlayerTestRunner implements Player.EventListener, ActionSc
}
@Override
public
void
onPositionDiscontinuity
(
@Player
.
DiscontinuityReason
int
reason
)
{
public
void
onPositionDiscontinuity
(
Player
.
PositionInfo
oldPosition
,
Player
.
PositionInfo
newPosition
,
@Player
.
DiscontinuityReason
int
reason
)
{
discontinuityReasons
.
add
(
reason
);
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
.
get
(
periodIndices
.
size
()
-
1
)
!=
currentIndex
)
{
// Ignore seek or internal discontinuities within a period.
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment