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
Show whitespace changes
Inline
Side-by-side
Showing
26 changed files
with
1583 additions
and
189 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 @@
...
@@ -2,6 +2,17 @@
### dev-v2 (not yet released)
### dev-v2 (not yet released)
*
Core Library:
*
Add position info of the old and the new position as arguments to
`EventListener.onPositionDiscontinuity`
. Add the new reasons
`DISCONTINUITY_REASON_SKIP`
and
`DISCONTINUITY_REASON_REMOVE`
and rename
`DISCONTINUITY_REASON_PERIOD_TRANSITION`
to
`DISCONTINUITY_REASON_AUTO_TRANSITION`
. Remove
`DISCONTINUITY_REASON_AD_INSERTION`
for which
`DISCONTINUITY_REASON_AUTO_TRANSITION`
is used instead. Deprecate the
`onPositionDiscontinuity(int)`
callback
(
[
#6163
](
https://github.com/google/ExoPlayer/issues/6163
)
,
[
#4768
](
https://github.com/google/ExoPlayer/issues/4768
)
).
*
UI:
*
UI:
*
Add builder for
`PlayerNotificationManager`
.
*
Add builder for
`PlayerNotificationManager`
.
*
Add group setting to
`PlayerNotificationManager`
.
*
Add group setting to
`PlayerNotificationManager`
.
...
...
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 {
...
@@ -460,6 +460,7 @@ public final class CastPlayer extends BasePlayer {
pendingSeekCount
++;
pendingSeekCount
++;
pendingSeekWindowIndex
=
windowIndex
;
pendingSeekWindowIndex
=
windowIndex
;
pendingSeekPositionMs
=
positionMs
;
pendingSeekPositionMs
=
positionMs
;
// TODO(b/181262841): call new onPositionDiscontinuity callback
listeners
.
queueEvent
(
listeners
.
queueEvent
(
Player
.
EVENT_POSITION_DISCONTINUITY
,
Player
.
EVENT_POSITION_DISCONTINUITY
,
listener
->
listener
.
onPositionDiscontinuity
(
DISCONTINUITY_REASON_SEEK
));
listener
->
listener
.
onPositionDiscontinuity
(
DISCONTINUITY_REASON_SEEK
));
...
@@ -630,6 +631,8 @@ public final class CastPlayer extends BasePlayer {
...
@@ -630,6 +631,8 @@ public final class CastPlayer extends BasePlayer {
// Internal methods.
// Internal methods.
// Call deprecated callbacks.
@SuppressWarnings
(
"deprecation"
)
private
void
updateInternalStateAndNotifyIfChanged
()
{
private
void
updateInternalStateAndNotifyIfChanged
()
{
if
(
remoteMediaClient
==
null
)
{
if
(
remoteMediaClient
==
null
)
{
// There is no session. We leave the state of the player as it is now.
// There is no session. We leave the state of the player as it is now.
...
@@ -648,9 +651,10 @@ public final class CastPlayer extends BasePlayer {
...
@@ -648,9 +651,10 @@ public final class CastPlayer extends BasePlayer {
int
currentWindowIndex
=
fetchCurrentWindowIndex
(
remoteMediaClient
,
currentTimeline
);
int
currentWindowIndex
=
fetchCurrentWindowIndex
(
remoteMediaClient
,
currentTimeline
);
if
(
this
.
currentWindowIndex
!=
currentWindowIndex
&&
pendingSeekCount
==
0
)
{
if
(
this
.
currentWindowIndex
!=
currentWindowIndex
&&
pendingSeekCount
==
0
)
{
this
.
currentWindowIndex
=
currentWindowIndex
;
this
.
currentWindowIndex
=
currentWindowIndex
;
// TODO(b/181262841): call new onPositionDiscontinuity callback
listeners
.
queueEvent
(
listeners
.
queueEvent
(
Player
.
EVENT_POSITION_DISCONTINUITY
,
Player
.
EVENT_POSITION_DISCONTINUITY
,
listener
->
listener
.
onPositionDiscontinuity
(
DISCONTINUITY_REASON_
PERIOD
_TRANSITION
));
listener
->
listener
.
onPositionDiscontinuity
(
DISCONTINUITY_REASON_
AUTO
_TRANSITION
));
listeners
.
queueEvent
(
listeners
.
queueEvent
(
Player
.
EVENT_MEDIA_ITEM_TRANSITION
,
Player
.
EVENT_MEDIA_ITEM_TRANSITION
,
listener
->
listener
->
...
...
extensions/ima/src/main/java/com/google/android/exoplayer2/ext/ima/AdTagLoader.java
View file @
dc4148d5
...
@@ -468,7 +468,10 @@ import java.util.Map;
...
@@ -468,7 +468,10 @@ import java.util.Map;
}
}
@Override
@Override
public
void
onPositionDiscontinuity
(
@Player
.
DiscontinuityReason
int
reason
)
{
public
void
onPositionDiscontinuity
(
Player
.
PositionInfo
oldPosition
,
Player
.
PositionInfo
newPosition
,
@Player
.
DiscontinuityReason
int
reason
)
{
handleTimelineOrPositionChanged
();
handleTimelineOrPositionChanged
();
}
}
...
...
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 {
...
@@ -613,7 +613,10 @@ public final class ImaAdsLoader implements Player.EventListener, AdsLoader {
}
}
@Override
@Override
public
void
onPositionDiscontinuity
(
@Player
.
DiscontinuityReason
int
reason
)
{
public
void
onPositionDiscontinuity
(
Player
.
PositionInfo
oldPosition
,
Player
.
PositionInfo
newPosition
,
@Player
.
DiscontinuityReason
int
reason
)
{
maybeUpdateCurrentAdTagLoader
();
maybeUpdateCurrentAdTagLoader
();
maybePreloadNextPeriodAds
();
maybePreloadNextPeriodAds
();
}
}
...
...
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;
...
@@ -29,6 +29,8 @@ import com.google.android.exoplayer2.util.ListenerSet;
private
final
ListenerSet
<
EventListener
>
listeners
;
private
final
ListenerSet
<
EventListener
>
listeners
;
private
final
Timeline
.
Period
period
;
private
final
Timeline
.
Period
period
;
private
final
Object
windowUid
=
new
Object
();
private
final
Object
periodUid
=
new
Object
();
private
Timeline
timeline
;
private
Timeline
timeline
;
@Player
.
State
private
int
state
;
@Player
.
State
private
int
state
;
...
@@ -65,6 +67,16 @@ import com.google.android.exoplayer2.util.ListenerSet;
...
@@ -65,6 +67,16 @@ import com.google.android.exoplayer2.util.ListenerSet;
*/
*/
public
void
setPlayingContentPosition
(
int
periodIndex
,
long
positionMs
)
{
public
void
setPlayingContentPosition
(
int
periodIndex
,
long
positionMs
)
{
boolean
notify
=
isPlayingAd
;
boolean
notify
=
isPlayingAd
;
PositionInfo
oldPosition
=
new
PositionInfo
(
windowUid
,
/* windowIndex= */
0
,
periodUid
,
/* periodIndex= */
0
,
this
.
positionMs
,
this
.
contentPositionMs
,
this
.
adGroupIndex
,
this
.
adIndexInAdGroup
);
isPlayingAd
=
false
;
isPlayingAd
=
false
;
adGroupIndex
=
C
.
INDEX_UNSET
;
adGroupIndex
=
C
.
INDEX_UNSET
;
adIndexInAdGroup
=
C
.
INDEX_UNSET
;
adIndexInAdGroup
=
C
.
INDEX_UNSET
;
...
@@ -72,9 +84,21 @@ import com.google.android.exoplayer2.util.ListenerSet;
...
@@ -72,9 +84,21 @@ import com.google.android.exoplayer2.util.ListenerSet;
this
.
positionMs
=
positionMs
;
this
.
positionMs
=
positionMs
;
contentPositionMs
=
positionMs
;
contentPositionMs
=
positionMs
;
if
(
notify
)
{
if
(
notify
)
{
PositionInfo
newPosition
=
new
PositionInfo
(
windowUid
,
/* windowIndex= */
0
,
periodUid
,
/* periodIndex= */
0
,
positionMs
,
this
.
contentPositionMs
,
this
.
adGroupIndex
,
this
.
adIndexInAdGroup
);
listeners
.
sendEvent
(
listeners
.
sendEvent
(
Player
.
EVENT_POSITION_DISCONTINUITY
,
Player
.
EVENT_POSITION_DISCONTINUITY
,
listener
->
listener
.
onPositionDiscontinuity
(
DISCONTINUITY_REASON_AD_INSERTION
));
listener
->
listener
.
onPositionDiscontinuity
(
oldPosition
,
newPosition
,
DISCONTINUITY_REASON_AUTO_TRANSITION
));
}
}
}
}
...
@@ -90,6 +114,16 @@ import com.google.android.exoplayer2.util.ListenerSet;
...
@@ -90,6 +114,16 @@ import com.google.android.exoplayer2.util.ListenerSet;
long
positionMs
,
long
positionMs
,
long
contentPositionMs
)
{
long
contentPositionMs
)
{
boolean
notify
=
!
isPlayingAd
||
this
.
adIndexInAdGroup
!=
adIndexInAdGroup
;
boolean
notify
=
!
isPlayingAd
||
this
.
adIndexInAdGroup
!=
adIndexInAdGroup
;
PositionInfo
oldPosition
=
new
PositionInfo
(
windowUid
,
/* windowIndex= */
0
,
periodUid
,
/* periodIndex= */
0
,
this
.
positionMs
,
this
.
contentPositionMs
,
this
.
adGroupIndex
,
this
.
adIndexInAdGroup
);
isPlayingAd
=
true
;
isPlayingAd
=
true
;
this
.
periodIndex
=
periodIndex
;
this
.
periodIndex
=
periodIndex
;
this
.
adGroupIndex
=
adGroupIndex
;
this
.
adGroupIndex
=
adGroupIndex
;
...
@@ -97,9 +131,21 @@ import com.google.android.exoplayer2.util.ListenerSet;
...
@@ -97,9 +131,21 @@ import com.google.android.exoplayer2.util.ListenerSet;
this
.
positionMs
=
positionMs
;
this
.
positionMs
=
positionMs
;
this
.
contentPositionMs
=
contentPositionMs
;
this
.
contentPositionMs
=
contentPositionMs
;
if
(
notify
)
{
if
(
notify
)
{
PositionInfo
newPosition
=
new
PositionInfo
(
windowUid
,
/* windowIndex= */
0
,
periodUid
,
/* periodIndex= */
0
,
positionMs
,
contentPositionMs
,
adGroupIndex
,
adIndexInAdGroup
);
listeners
.
sendEvent
(
listeners
.
sendEvent
(
EVENT_POSITION_DISCONTINUITY
,
EVENT_POSITION_DISCONTINUITY
,
listener
->
listener
.
onPositionDiscontinuity
(
DISCONTINUITY_REASON_AD_INSERTION
));
listener
->
listener
.
onPositionDiscontinuity
(
oldPosition
,
newPosition
,
DISCONTINUITY_REASON_AUTO_TRANSITION
));
}
}
}
}
...
...
extensions/ima/src/test/java/com/google/android/exoplayer2/ext/ima/ImaAdsLoaderTest.java
View file @
dc4148d5
...
@@ -281,7 +281,26 @@ public final class ImaAdsLoaderTest {
...
@@ -281,7 +281,26 @@ public final class ImaAdsLoaderTest {
videoAdPlayer
.
pauseAd
(
TEST_AD_MEDIA_INFO
);
videoAdPlayer
.
pauseAd
(
TEST_AD_MEDIA_INFO
);
videoAdPlayer
.
stopAd
(
TEST_AD_MEDIA_INFO
);
videoAdPlayer
.
stopAd
(
TEST_AD_MEDIA_INFO
);
imaAdsLoader
.
onPlayerError
(
ExoPlaybackException
.
createForSource
(
new
IOException
()));
imaAdsLoader
.
onPlayerError
(
ExoPlaybackException
.
createForSource
(
new
IOException
()));
imaAdsLoader
.
onPositionDiscontinuity
(
Player
.
DISCONTINUITY_REASON_SEEK
);
imaAdsLoader
.
onPositionDiscontinuity
(
new
Player
.
PositionInfo
(
/* windowUid= */
new
Object
(),
/* windowIndex= */
0
,
/* periodUid= */
new
Object
(),
/* periodIndex= */
0
,
/* positionMs= */
10_000
,
/* contentPositionMs= */
0
,
/* adGroupIndex= */
-
1
,
/* adIndexInAdGroup= */
-
1
),
new
Player
.
PositionInfo
(
/* windowUid= */
new
Object
(),
/* windowIndex= */
1
,
/* periodUid= */
new
Object
(),
/* periodIndex= */
0
,
/* positionMs= */
20_000
,
/* contentPositionMs= */
0
,
/* adGroupIndex= */
-
1
,
/* adIndexInAdGroup= */
-
1
),
Player
.
DISCONTINUITY_REASON_SEEK
);
adEventListener
.
onAdEvent
(
getAdEvent
(
AdEventType
.
CONTENT_RESUME_REQUESTED
,
/* ad= */
null
));
adEventListener
.
onAdEvent
(
getAdEvent
(
AdEventType
.
CONTENT_RESUME_REQUESTED
,
/* ad= */
null
));
imaAdsLoader
.
handlePrepareError
(
imaAdsLoader
.
handlePrepareError
(
adsMediaSource
,
/* adGroupIndex= */
0
,
/* adIndexInAdGroup= */
0
,
new
IOException
());
adsMediaSource
,
/* adGroupIndex= */
0
,
/* adIndexInAdGroup= */
0
,
new
IOException
());
...
...
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
...
@@ -306,7 +306,10 @@ public final class LeanbackPlayerAdapter extends PlayerAdapter implements Runnab
}
}
@Override
@Override
public
void
onPositionDiscontinuity
(
@DiscontinuityReason
int
reason
)
{
public
void
onPositionDiscontinuity
(
Player
.
PositionInfo
oldPosition
,
Player
.
PositionInfo
newPosition
,
@DiscontinuityReason
int
reason
)
{
Callback
callback
=
getCallback
();
Callback
callback
=
getCallback
();
callback
.
onCurrentPositionChanged
(
LeanbackPlayerAdapter
.
this
);
callback
.
onCurrentPositionChanged
(
LeanbackPlayerAdapter
.
this
);
callback
.
onBufferedPositionChanged
(
LeanbackPlayerAdapter
.
this
);
callback
.
onBufferedPositionChanged
(
LeanbackPlayerAdapter
.
this
);
...
...
extensions/media2/src/main/java/com/google/android/exoplayer2/ext/media2/PlayerWrapper.java
View file @
dc4148d5
...
@@ -437,7 +437,7 @@ import java.util.List;
...
@@ -437,7 +437,7 @@ import java.util.List;
case
Player
.
STATE_READY
:
case
Player
.
STATE_READY
:
if
(!
prepared
)
{
if
(!
prepared
)
{
prepared
=
true
;
prepared
=
true
;
handlePositionDiscontinuity
(
Player
.
DISCONTINUITY_REASON_
PERIOD
_TRANSITION
);
handlePositionDiscontinuity
(
Player
.
DISCONTINUITY_REASON_
AUTO
_TRANSITION
);
listener
.
onPrepared
(
listener
.
onPrepared
(
Assertions
.
checkNotNull
(
getCurrentMediaItem
()),
player
.
getBufferedPercentage
());
Assertions
.
checkNotNull
(
getCurrentMediaItem
()),
player
.
getBufferedPercentage
());
}
}
...
@@ -517,9 +517,11 @@ import java.util.List;
...
@@ -517,9 +517,11 @@ import java.util.List;
int
currentWindowIndex
=
getCurrentMediaItemIndex
();
int
currentWindowIndex
=
getCurrentMediaItemIndex
();
if
(
this
.
currentWindowIndex
!=
currentWindowIndex
)
{
if
(
this
.
currentWindowIndex
!=
currentWindowIndex
)
{
this
.
currentWindowIndex
=
currentWindowIndex
;
this
.
currentWindowIndex
=
currentWindowIndex
;
if
(
currentWindowIndex
!=
C
.
INDEX_UNSET
)
{
androidx
.
media2
.
common
.
MediaItem
currentMediaItem
=
androidx
.
media2
.
common
.
MediaItem
currentMediaItem
=
Assertions
.
checkNotNull
(
getCurrentMediaItem
());
Assertions
.
checkNotNull
(
getCurrentMediaItem
());
listener
.
onCurrentMediaItemChanged
(
currentMediaItem
);
listener
.
onCurrentMediaItemChanged
(
currentMediaItem
);
}
}
else
{
}
else
{
listener
.
onSeekCompleted
();
listener
.
onSeekCompleted
();
}
}
...
@@ -597,7 +599,10 @@ import java.util.List;
...
@@ -597,7 +599,10 @@ import java.util.List;
}
}
@Override
@Override
public
void
onPositionDiscontinuity
(
@Player
.
DiscontinuityReason
int
reason
)
{
public
void
onPositionDiscontinuity
(
Player
.
PositionInfo
oldPosition
,
Player
.
PositionInfo
newPosition
,
@Player
.
DiscontinuityReason
int
reason
)
{
handlePositionDiscontinuity
(
reason
);
handlePositionDiscontinuity
(
reason
);
}
}
...
...
library/common/src/main/java/com/google/android/exoplayer2/Player.java
View file @
dc4148d5
...
@@ -15,7 +15,6 @@
...
@@ -15,7 +15,6 @@
*/
*/
package
com
.
google
.
android
.
exoplayer2
;
package
com
.
google
.
android
.
exoplayer2
;
import
android.content.Context
;
import
android.content.Context
;
import
android.os.Looper
;
import
android.os.Looper
;
import
android.view.Surface
;
import
android.view.Surface
;
...
@@ -40,6 +39,7 @@ import com.google.android.exoplayer2.util.Util;
...
@@ -40,6 +39,7 @@ import com.google.android.exoplayer2.util.Util;
import
com.google.android.exoplayer2.video.VideoFrameMetadataListener
;
import
com.google.android.exoplayer2.video.VideoFrameMetadataListener
;
import
com.google.android.exoplayer2.video.VideoListener
;
import
com.google.android.exoplayer2.video.VideoListener
;
import
com.google.android.exoplayer2.video.spherical.CameraMotionListener
;
import
com.google.android.exoplayer2.video.spherical.CameraMotionListener
;
import
com.google.common.base.Objects
;
import
java.lang.annotation.Documented
;
import
java.lang.annotation.Documented
;
import
java.lang.annotation.Retention
;
import
java.lang.annotation.Retention
;
import
java.lang.annotation.RetentionPolicy
;
import
java.lang.annotation.RetentionPolicy
;
...
@@ -396,10 +396,9 @@ public interface Player {
...
@@ -396,10 +396,9 @@ public interface Player {
/**
/**
* Called when the timeline has been refreshed.
* Called when the timeline has been refreshed.
*
*
* <p>Note that if the timeline has changed then a position discontinuity may also have
* <p>Note that the current window or period index may change as a result of a timeline change.
* occurred. For example, the current period index may have changed as a result of periods being
* If playback can't continue smoothly because of this timeline change, a separate {@link
* added or removed from the timeline. This will <em>not</em> be reported via a separate call to
* #onPositionDiscontinuity(PositionInfo, PositionInfo, int)} callback will be triggered.
* {@link #onPositionDiscontinuity(int)}.
*
*
* <p>{@link #onEvents(Player, Events)} will also be called to report this event along with
* <p>{@link #onEvents(Player, Events)} will also be called to report this event along with
* other events that happen in the same {@link Looper} message queue iteration.
* other events that happen in the same {@link Looper} message queue iteration.
...
@@ -576,21 +575,27 @@ public interface Player {
...
@@ -576,21 +575,27 @@ public interface Player {
default
void
onPlayerError
(
ExoPlaybackException
error
)
{}
default
void
onPlayerError
(
ExoPlaybackException
error
)
{}
/**
/**
* Called when a position discontinuity occurs without a change to the timeline. A position
* @deprecated Use {@link #onPositionDiscontinuity(PositionInfo, PositionInfo, int)} instead.
* discontinuity occurs when the current window or period index changes (as a result of playback
*/
* transitioning from one period in the timeline to the next), or when the playback position
@Deprecated
* jumps within the period currently being played (as a result of a seek being performed, or
default
void
onPositionDiscontinuity
(
@DiscontinuityReason
int
reason
)
{}
* when the source introduces a discontinuity internally).
/**
* Called when a position discontinuity occurs.
*
*
* <p>When a position discontinuity occurs as a result of a change to the timeline this method
* <p>A position discontinuity occurs when the playing period changes, the playback position
* is <em>not</em> called. {@link #onTimelineChanged(Timeline, int)} is called in this case.
* jumps within the period currently being played, or when the playing period has been skipped
* or removed.
*
*
* <p>{@link #onEvents(Player, Events)} will also be called to report this event along with
* <p>{@link #onEvents(Player, Events)} will also be called to report this event along with
* other events that happen in the same {@link Looper} message queue iteration.
* other events that happen in the same {@link Looper} message queue iteration.
*
*
* @param oldPosition The position before the discontinuity.
* @param newPosition The position after the discontinuity.
* @param reason The {@link DiscontinuityReason} responsible for the discontinuity.
* @param reason The {@link DiscontinuityReason} responsible for the discontinuity.
*/
*/
default
void
onPositionDiscontinuity
(
@DiscontinuityReason
int
reason
)
{}
default
void
onPositionDiscontinuity
(
PositionInfo
oldPosition
,
PositionInfo
newPosition
,
@DiscontinuityReason
int
reason
)
{}
/**
/**
* Called when the current playback parameters change. The playback parameters may change due to
* Called when the current playback parameters change. The playback parameters may change due to
...
@@ -607,7 +612,8 @@ public interface Player {
...
@@ -607,7 +612,8 @@ public interface Player {
/**
/**
* @deprecated Seeks are processed without delay. Listen to {@link
* @deprecated Seeks are processed without delay. Listen to {@link
* #onPositionDiscontinuity(int)} with reason {@link #DISCONTINUITY_REASON_SEEK} instead.
* #onPositionDiscontinuity(PositionInfo, PositionInfo, int)} with reason {@link
* #DISCONTINUITY_REASON_SEEK} instead.
*/
*/
@Deprecated
@Deprecated
default
void
onSeekProcessed
()
{}
default
void
onSeekProcessed
()
{}
...
@@ -698,6 +704,94 @@ public interface Player {
...
@@ -698,6 +704,94 @@ public interface Player {
}
}
}
}
/** Position info describing a playback position involved in a discontinuity. */
final
class
PositionInfo
{
/**
* The UID of the window, or {@code null}, if the timeline is {@link Timeline#isEmpty() empty}.
*/
@Nullable
public
final
Object
windowUid
;
/** The window index. */
public
final
int
windowIndex
;
/**
* The UID of the period, or {@code null}, if the timeline is {@link Timeline#isEmpty() empty}.
*/
@Nullable
public
final
Object
periodUid
;
/** The period index. */
public
final
int
periodIndex
;
/** The playback position, in milliseconds. */
public
final
long
positionMs
;
/**
* The content position, in milliseconds.
*
* <p>If {@link #adGroupIndex} is {@link C#INDEX_UNSET}, this is the same as {@link
* #positionMs}.
*/
public
final
long
contentPositionMs
;
/**
* The ad group index if the playback position is within an ad, {@link C#INDEX_UNSET} otherwise.
*/
public
final
int
adGroupIndex
;
/**
* The index of the ad within the ad group if the playback position is within an ad, {@link
* C#INDEX_UNSET} otherwise.
*/
public
final
int
adIndexInAdGroup
;
/** Creates an instance. */
public
PositionInfo
(
@Nullable
Object
windowUid
,
int
windowIndex
,
@Nullable
Object
periodUid
,
int
periodIndex
,
long
positionMs
,
long
contentPositionMs
,
int
adGroupIndex
,
int
adIndexInAdGroup
)
{
this
.
windowUid
=
windowUid
;
this
.
windowIndex
=
windowIndex
;
this
.
periodUid
=
periodUid
;
this
.
periodIndex
=
periodIndex
;
this
.
positionMs
=
positionMs
;
this
.
contentPositionMs
=
contentPositionMs
;
this
.
adGroupIndex
=
adGroupIndex
;
this
.
adIndexInAdGroup
=
adIndexInAdGroup
;
}
@Override
public
boolean
equals
(
@Nullable
Object
o
)
{
if
(
this
==
o
)
{
return
true
;
}
if
(
o
==
null
||
getClass
()
!=
o
.
getClass
())
{
return
false
;
}
PositionInfo
that
=
(
PositionInfo
)
o
;
return
windowIndex
==
that
.
windowIndex
&&
periodIndex
==
that
.
periodIndex
&&
positionMs
==
that
.
positionMs
&&
contentPositionMs
==
that
.
contentPositionMs
&&
adGroupIndex
==
that
.
adGroupIndex
&&
adIndexInAdGroup
==
that
.
adIndexInAdGroup
&&
Objects
.
equal
(
windowUid
,
that
.
windowUid
)
&&
Objects
.
equal
(
periodUid
,
that
.
periodUid
);
}
@Override
public
int
hashCode
()
{
return
Objects
.
hashCode
(
windowUid
,
windowIndex
,
periodUid
,
periodIndex
,
windowIndex
,
positionMs
,
contentPositionMs
,
adGroupIndex
,
adIndexInAdGroup
);
}
}
/**
/**
* A set of {@link Command commands}.
* A set of {@link Command commands}.
*
*
...
@@ -933,25 +1027,30 @@ public interface Player {
...
@@ -933,25 +1027,30 @@ public interface Player {
int
REPEAT_MODE_ALL
=
2
;
int
REPEAT_MODE_ALL
=
2
;
/**
/**
* Reasons for position discontinuities. One of {@link #DISCONTINUITY_REASON_
PERIOD
_TRANSITION},
* Reasons for position discontinuities. One of {@link #DISCONTINUITY_REASON_
AUTO
_TRANSITION},
* {@link #DISCONTINUITY_REASON_SEEK}, {@link #DISCONTINUITY_REASON_SEEK_ADJUSTMENT}, {@link
* {@link #DISCONTINUITY_REASON_SEEK}, {@link #DISCONTINUITY_REASON_SEEK_ADJUSTMENT}, {@link
* #DISCONTINUITY_REASON_AD_INSERTION} or {@link #DISCONTINUITY_REASON_INTERNAL}.
* #DISCONTINUITY_REASON_SKIP}, {@link #DISCONTINUITY_REASON_REMOVE} or {@link
* #DISCONTINUITY_REASON_INTERNAL}.
*/
*/
@Documented
@Documented
@Retention
(
RetentionPolicy
.
SOURCE
)
@Retention
(
RetentionPolicy
.
SOURCE
)
@IntDef
({
@IntDef
({
DISCONTINUITY_REASON_
PERIOD
_TRANSITION
,
DISCONTINUITY_REASON_
AUTO
_TRANSITION
,
DISCONTINUITY_REASON_SEEK
,
DISCONTINUITY_REASON_SEEK
,
DISCONTINUITY_REASON_SEEK_ADJUSTMENT
,
DISCONTINUITY_REASON_SEEK_ADJUSTMENT
,
DISCONTINUITY_REASON_AD_INSERTION
,
DISCONTINUITY_REASON_SKIP
,
DISCONTINUITY_REASON_REMOVE
,
DISCONTINUITY_REASON_INTERNAL
DISCONTINUITY_REASON_INTERNAL
})
})
@interface
DiscontinuityReason
{}
@interface
DiscontinuityReason
{}
/**
/**
* Automatic playback transition from one period in the timeline to the next. The period index may
* Automatic playback transition from one period in the timeline to the next. The period index may
* be the same as it was before the discontinuity in case the current period is repeated.
* be the same as it was before the discontinuity in case the current period is repeated.
*
* <p>This reason also indicates an automatic transition from the content period to an inserted ad
* period or vice versa.
*/
*/
int
DISCONTINUITY_REASON_
PERIOD
_TRANSITION
=
0
;
int
DISCONTINUITY_REASON_
AUTO
_TRANSITION
=
0
;
/** Seek within the current period or to another period. */
/** Seek within the current period or to another period. */
int
DISCONTINUITY_REASON_SEEK
=
1
;
int
DISCONTINUITY_REASON_SEEK
=
1
;
/**
/**
...
@@ -959,10 +1058,12 @@ public interface Player {
...
@@ -959,10 +1058,12 @@ public interface Player {
* permitted to be inexact.
* permitted to be inexact.
*/
*/
int
DISCONTINUITY_REASON_SEEK_ADJUSTMENT
=
2
;
int
DISCONTINUITY_REASON_SEEK_ADJUSTMENT
=
2
;
/** Discontinuity to or from an ad within one period in the timeline. */
/** Discontinuity introduced by a skipped period (for instance a skipped ad). */
int
DISCONTINUITY_REASON_AD_INSERTION
=
3
;
int
DISCONTINUITY_REASON_SKIP
=
3
;
/** Discontinuity caused by the removal of the current period from the {@link Timeline}. */
int
DISCONTINUITY_REASON_REMOVE
=
4
;
/** Discontinuity introduced internally by the source. */
/** Discontinuity introduced internally by the source. */
int
DISCONTINUITY_REASON_INTERNAL
=
4
;
int
DISCONTINUITY_REASON_INTERNAL
=
5
;
/**
/**
* Reasons for timeline changes. One of {@link #TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED} or {@link
* Reasons for timeline changes. One of {@link #TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED} or {@link
...
@@ -1053,7 +1154,10 @@ public interface Player {
...
@@ -1053,7 +1154,10 @@ public interface Player {
int
EVENT_SHUFFLE_MODE_ENABLED_CHANGED
=
10
;
int
EVENT_SHUFFLE_MODE_ENABLED_CHANGED
=
10
;
/** {@link #getPlayerError()} changed. */
/** {@link #getPlayerError()} changed. */
int
EVENT_PLAYER_ERROR
=
11
;
int
EVENT_PLAYER_ERROR
=
11
;
/** A position discontinuity occurred. See {@link EventListener#onPositionDiscontinuity(int)}. */
/**
* A position discontinuity occurred. See {@link
* EventListener#onPositionDiscontinuity(PositionInfo, PositionInfo, int)}.
*/
int
EVENT_POSITION_DISCONTINUITY
=
12
;
int
EVENT_POSITION_DISCONTINUITY
=
12
;
/** {@link #getPlaybackParameters()} changed. */
/** {@link #getPlaybackParameters()} changed. */
int
EVENT_PLAYBACK_PARAMETERS_CHANGED
=
13
;
int
EVENT_PLAYBACK_PARAMETERS_CHANGED
=
13
;
...
...
library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java
View file @
dc4148d5
...
@@ -87,8 +87,8 @@ import java.util.concurrent.CopyOnWriteArraySet;
...
@@ -87,8 +87,8 @@ import java.util.concurrent.CopyOnWriteArraySet;
@RepeatMode
private
int
repeatMode
;
@RepeatMode
private
int
repeatMode
;
private
boolean
shuffleModeEnabled
;
private
boolean
shuffleModeEnabled
;
private
int
pendingOperationAcks
;
private
int
pendingOperationAcks
;
private
boolean
hasPendingDiscontinuity
;
@DiscontinuityReason
private
int
pendingDiscontinuityReason
;
@DiscontinuityReason
private
int
pendingDiscontinuityReason
;
private
boolean
pendingDiscontinuity
;
@PlayWhenReadyChangeReason
private
int
pendingPlayWhenReadyChangeReason
;
@PlayWhenReadyChangeReason
private
int
pendingPlayWhenReadyChangeReason
;
private
boolean
foregroundMode
;
private
boolean
foregroundMode
;
private
SeekParameters
seekParameters
;
private
SeekParameters
seekParameters
;
...
@@ -367,11 +367,13 @@ import java.util.concurrent.CopyOnWriteArraySet;
...
@@ -367,11 +367,13 @@ import java.util.concurrent.CopyOnWriteArraySet;
internalPlayer
.
prepare
();
internalPlayer
.
prepare
();
updatePlaybackInfo
(
updatePlaybackInfo
(
playbackInfo
,
playbackInfo
,
/* positionDiscontinuity= */
false
,
/* ignored */
DISCONTINUITY_REASON_INTERNAL
,
/* ignored */
TIMELINE_CHANGE_REASON_SOURCE_UPDATE
,
/* ignored */
TIMELINE_CHANGE_REASON_SOURCE_UPDATE
,
/* ignored */
PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST
,
/* 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;
...
@@ -479,24 +481,30 @@ import java.util.concurrent.CopyOnWriteArraySet;
internalPlayer
.
addMediaSources
(
index
,
holders
,
shuffleOrder
);
internalPlayer
.
addMediaSources
(
index
,
holders
,
shuffleOrder
);
updatePlaybackInfo
(
updatePlaybackInfo
(
newPlaybackInfo
,
newPlaybackInfo
,
/* positionDiscontinuity= */
false
,
/* ignored */
DISCONTINUITY_REASON_INTERNAL
,
/* timelineChangeReason= */
TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED
,
/* timelineChangeReason= */
TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED
,
/* ignored */
PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST
,
/* 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
@Override
public
void
removeMediaItems
(
int
fromIndex
,
int
toIndex
)
{
public
void
removeMediaItems
(
int
fromIndex
,
int
toIndex
)
{
toIndex
=
min
(
toIndex
,
mediaSourceHolderSnapshots
.
size
());
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
(
updatePlaybackInfo
(
playbackInfo
,
newPlaybackInfo
,
/* positionDiscontinuity= */
false
,
/* ignored */
Player
.
DISCONTINUITY_REASON_INTERNAL
,
/* timelineChangeReason= */
TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED
,
/* timelineChangeReason= */
TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED
,
/* ignored */
PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST
,
/* 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
@Override
...
@@ -519,11 +527,13 @@ import java.util.concurrent.CopyOnWriteArraySet;
...
@@ -519,11 +527,13 @@ import java.util.concurrent.CopyOnWriteArraySet;
internalPlayer
.
moveMediaSources
(
fromIndex
,
toIndex
,
newFromIndex
,
shuffleOrder
);
internalPlayer
.
moveMediaSources
(
fromIndex
,
toIndex
,
newFromIndex
,
shuffleOrder
);
updatePlaybackInfo
(
updatePlaybackInfo
(
newPlaybackInfo
,
newPlaybackInfo
,
/* positionDiscontinuity= */
false
,
/* ignored */
DISCONTINUITY_REASON_INTERNAL
,
/* timelineChangeReason= */
TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED
,
/* timelineChangeReason= */
TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED
,
/* ignored */
PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST
,
/* 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
@Override
...
@@ -540,11 +550,13 @@ import java.util.concurrent.CopyOnWriteArraySet;
...
@@ -540,11 +550,13 @@ import java.util.concurrent.CopyOnWriteArraySet;
internalPlayer
.
setShuffleOrder
(
shuffleOrder
);
internalPlayer
.
setShuffleOrder
(
shuffleOrder
);
updatePlaybackInfo
(
updatePlaybackInfo
(
newPlaybackInfo
,
newPlaybackInfo
,
/* positionDiscontinuity= */
false
,
/* ignored */
DISCONTINUITY_REASON_INTERNAL
,
/* timelineChangeReason= */
TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED
,
/* timelineChangeReason= */
TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED
,
/* ignored */
PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST
,
/* 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
@Override
...
@@ -583,11 +595,13 @@ import java.util.concurrent.CopyOnWriteArraySet;
...
@@ -583,11 +595,13 @@ import java.util.concurrent.CopyOnWriteArraySet;
internalPlayer
.
setPlayWhenReady
(
playWhenReady
,
playbackSuppressionReason
);
internalPlayer
.
setPlayWhenReady
(
playWhenReady
,
playbackSuppressionReason
);
updatePlaybackInfo
(
updatePlaybackInfo
(
playbackInfo
,
playbackInfo
,
/* positionDiscontinuity= */
false
,
/* ignored */
DISCONTINUITY_REASON_INTERNAL
,
/* ignored */
TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED
,
/* ignored */
TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED
,
playWhenReadyChangeReason
,
playWhenReadyChangeReason
,
/* seekProcessed= */
false
);
/* seekProcessed= */
false
,
/* positionDiscontinuity= */
false
,
/* ignored */
DISCONTINUITY_REASON_INTERNAL
,
/* ignored */
C
.
TIME_UNSET
,
/* ignored */
C
.
INDEX_UNSET
);
}
}
@Override
@Override
...
@@ -656,7 +670,8 @@ import java.util.concurrent.CopyOnWriteArraySet;
...
@@ -656,7 +670,8 @@ import java.util.concurrent.CopyOnWriteArraySet;
@Player
.
State
@Player
.
State
int
newPlaybackState
=
int
newPlaybackState
=
getPlaybackState
()
==
Player
.
STATE_IDLE
?
Player
.
STATE_IDLE
:
Player
.
STATE_BUFFERING
;
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
=
newPlaybackInfo
=
maskTimelineAndPosition
(
maskTimelineAndPosition
(
newPlaybackInfo
,
newPlaybackInfo
,
...
@@ -665,11 +680,13 @@ import java.util.concurrent.CopyOnWriteArraySet;
...
@@ -665,11 +680,13 @@ import java.util.concurrent.CopyOnWriteArraySet;
internalPlayer
.
seekTo
(
timeline
,
windowIndex
,
C
.
msToUs
(
positionMs
));
internalPlayer
.
seekTo
(
timeline
,
windowIndex
,
C
.
msToUs
(
positionMs
));
updatePlaybackInfo
(
updatePlaybackInfo
(
newPlaybackInfo
,
newPlaybackInfo
,
/* positionDiscontinuity= */
true
,
/* positionDiscontinuityReason= */
DISCONTINUITY_REASON_SEEK
,
/* ignored */
TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED
,
/* ignored */
TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED
,
/* ignored */
PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST
,
/* ignored */
PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST
,
/* seekProcessed= */
true
);
/* seekProcessed= */
true
,
/* positionDiscontinuity= */
true
,
/* positionDiscontinuityReason= */
DISCONTINUITY_REASON_SEEK
,
/* discontinuityWindowStartPositionUs= */
getCurrentPositionUsInternal
(
newPlaybackInfo
),
oldMaskingWindowIndex
);
}
}
@Override
@Override
...
@@ -685,11 +702,13 @@ import java.util.concurrent.CopyOnWriteArraySet;
...
@@ -685,11 +702,13 @@ import java.util.concurrent.CopyOnWriteArraySet;
internalPlayer
.
setPlaybackParameters
(
playbackParameters
);
internalPlayer
.
setPlaybackParameters
(
playbackParameters
);
updatePlaybackInfo
(
updatePlaybackInfo
(
newPlaybackInfo
,
newPlaybackInfo
,
/* positionDiscontinuity= */
false
,
/* ignored */
DISCONTINUITY_REASON_INTERNAL
,
/* ignored */
TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED
,
/* ignored */
TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED
,
/* ignored */
PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST
,
/* 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
@Override
...
@@ -758,13 +777,17 @@ import java.util.concurrent.CopyOnWriteArraySet;
...
@@ -758,13 +777,17 @@ import java.util.concurrent.CopyOnWriteArraySet;
}
}
pendingOperationAcks
++;
pendingOperationAcks
++;
internalPlayer
.
stop
();
internalPlayer
.
stop
();
boolean
positionDiscontinuity
=
playbackInfo
.
timeline
.
isEmpty
()
&&
!
this
.
playbackInfo
.
timeline
.
isEmpty
();
updatePlaybackInfo
(
updatePlaybackInfo
(
playbackInfo
,
playbackInfo
,
/* positionDiscontinuity= */
false
,
/* ignored */
DISCONTINUITY_REASON_INTERNAL
,
TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED
,
TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED
,
/* ignored */
PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST
,
/* ignored */
PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST
,
/* seekProcessed= */
false
);
/* seekProcessed= */
false
,
positionDiscontinuity
,
DISCONTINUITY_REASON_REMOVE
,
/* discontinuityWindowStartPositionUs= */
getCurrentPositionUsInternal
(
playbackInfo
),
/* ignored */
C
.
INDEX_UNSET
);
}
}
@Override
@Override
...
@@ -839,13 +862,7 @@ import java.util.concurrent.CopyOnWriteArraySet;
...
@@ -839,13 +862,7 @@ import java.util.concurrent.CopyOnWriteArraySet;
@Override
@Override
public
long
getCurrentPosition
()
{
public
long
getCurrentPosition
()
{
if
(
playbackInfo
.
timeline
.
isEmpty
())
{
return
C
.
usToMs
(
getCurrentPositionUsInternal
(
playbackInfo
));
return
maskingWindowPositionMs
;
}
else
if
(
playbackInfo
.
periodId
.
isAd
())
{
return
C
.
usToMs
(
playbackInfo
.
positionUs
);
}
else
{
return
periodPositionUsToWindowPositionMs
(
playbackInfo
.
periodId
,
playbackInfo
.
positionUs
);
}
}
}
@Override
@Override
...
@@ -909,8 +926,9 @@ import java.util.concurrent.CopyOnWriteArraySet;
...
@@ -909,8 +926,9 @@ import java.util.concurrent.CopyOnWriteArraySet;
contentBufferedPositionUs
=
loadingPeriod
.
durationUs
;
contentBufferedPositionUs
=
loadingPeriod
.
durationUs
;
}
}
}
}
return
periodPositionUsToWindowPositionMs
(
return
C
.
usToMs
(
playbackInfo
.
loadingMediaPeriodId
,
contentBufferedPositionUs
);
periodPositionUsToWindowPositionUs
(
playbackInfo
.
timeline
,
playbackInfo
.
loadingMediaPeriodId
,
contentBufferedPositionUs
));
}
}
@Override
@Override
...
@@ -958,6 +976,17 @@ import java.util.concurrent.CopyOnWriteArraySet;
...
@@ -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
)
{
private
List
<
MediaSource
>
createMediaSources
(
List
<
MediaItem
>
mediaItems
)
{
List
<
MediaSource
>
mediaSources
=
new
ArrayList
<>();
List
<
MediaSource
>
mediaSources
=
new
ArrayList
<>();
for
(
int
i
=
0
;
i
<
mediaItems
.
size
();
i
++)
{
for
(
int
i
=
0
;
i
<
mediaItems
.
size
();
i
++)
{
...
@@ -969,8 +998,8 @@ import java.util.concurrent.CopyOnWriteArraySet;
...
@@ -969,8 +998,8 @@ import java.util.concurrent.CopyOnWriteArraySet;
private
void
handlePlaybackInfo
(
ExoPlayerImplInternal
.
PlaybackInfoUpdate
playbackInfoUpdate
)
{
private
void
handlePlaybackInfo
(
ExoPlayerImplInternal
.
PlaybackInfoUpdate
playbackInfoUpdate
)
{
pendingOperationAcks
-=
playbackInfoUpdate
.
operationAcks
;
pendingOperationAcks
-=
playbackInfoUpdate
.
operationAcks
;
if
(
playbackInfoUpdate
.
positionDiscontinuity
)
{
if
(
playbackInfoUpdate
.
positionDiscontinuity
)
{
hasPendingDiscontinuity
=
true
;
pendingDiscontinuityReason
=
playbackInfoUpdate
.
discontinuityReason
;
pendingDiscontinuityReason
=
playbackInfoUpdate
.
discontinuityReason
;
pendingDiscontinuity
=
true
;
}
}
if
(
playbackInfoUpdate
.
hasPlayWhenReadyChangeReason
)
{
if
(
playbackInfoUpdate
.
hasPlayWhenReadyChangeReason
)
{
pendingPlayWhenReadyChangeReason
=
playbackInfoUpdate
.
playWhenReadyChangeReason
;
pendingPlayWhenReadyChangeReason
=
playbackInfoUpdate
.
playWhenReadyChangeReason
;
...
@@ -991,15 +1020,33 @@ import java.util.concurrent.CopyOnWriteArraySet;
...
@@ -991,15 +1020,33 @@ import java.util.concurrent.CopyOnWriteArraySet;
mediaSourceHolderSnapshots
.
get
(
i
).
timeline
=
timelines
.
get
(
i
);
mediaSourceHolderSnapshots
.
get
(
i
).
timeline
=
timelines
.
get
(
i
);
}
}
}
}
boolean
positionDiscontinuity
=
hasPendingDiscontinuity
;
boolean
positionDiscontinuity
=
false
;
hasPendingDiscontinuity
=
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
(
updatePlaybackInfo
(
playbackInfoUpdate
.
playbackInfo
,
playbackInfoUpdate
.
playbackInfo
,
positionDiscontinuity
,
pendingDiscontinuityReason
,
TIMELINE_CHANGE_REASON_SOURCE_UPDATE
,
TIMELINE_CHANGE_REASON_SOURCE_UPDATE
,
pendingPlayWhenReadyChangeReason
,
pendingPlayWhenReadyChangeReason
,
/* seekProcessed= */
false
);
/* seekProcessed= */
false
,
positionDiscontinuity
,
pendingDiscontinuityReason
,
discontinuityWindowStartPositionUs
,
/* ignored */
C
.
INDEX_UNSET
);
}
}
}
}
...
@@ -1007,11 +1054,14 @@ import java.util.concurrent.CopyOnWriteArraySet;
...
@@ -1007,11 +1054,14 @@ import java.util.concurrent.CopyOnWriteArraySet;
@SuppressWarnings
(
"deprecation"
)
@SuppressWarnings
(
"deprecation"
)
private
void
updatePlaybackInfo
(
private
void
updatePlaybackInfo
(
PlaybackInfo
playbackInfo
,
PlaybackInfo
playbackInfo
,
boolean
positionDiscontinuity
,
@DiscontinuityReason
int
positionDiscontinuityReason
,
@TimelineChangeReason
int
timelineChangeReason
,
@TimelineChangeReason
int
timelineChangeReason
,
@PlayWhenReadyChangeReason
int
playWhenReadyChangeReason
,
@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
// 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.
// snapshot of previous and new state so that listener invocations are triggered correctly.
PlaybackInfo
previousPlaybackInfo
=
this
.
playbackInfo
;
PlaybackInfo
previousPlaybackInfo
=
this
.
playbackInfo
;
...
@@ -1042,9 +1092,17 @@ import java.util.concurrent.CopyOnWriteArraySet;
...
@@ -1042,9 +1092,17 @@ import java.util.concurrent.CopyOnWriteArraySet;
});
});
}
}
if
(
positionDiscontinuity
)
{
if
(
positionDiscontinuity
)
{
PositionInfo
previousPositionInfo
=
getPreviousPositionInfo
(
positionDiscontinuityReason
,
previousPlaybackInfo
,
oldMaskingWindowIndex
);
PositionInfo
positionInfo
=
getPositionInfo
(
discontinuityWindowStartPositionUs
);
listeners
.
queueEvent
(
listeners
.
queueEvent
(
Player
.
EVENT_POSITION_DISCONTINUITY
,
Player
.
EVENT_POSITION_DISCONTINUITY
,
listener
->
listener
.
onPositionDiscontinuity
(
positionDiscontinuityReason
));
listener
->
{
listener
.
onPositionDiscontinuity
(
positionDiscontinuityReason
);
listener
.
onPositionDiscontinuity
(
previousPositionInfo
,
positionInfo
,
positionDiscontinuityReason
);
});
}
}
if
(
mediaItemTransitioned
)
{
if
(
mediaItemTransitioned
)
{
@Nullable
final
MediaItem
mediaItem
;
@Nullable
final
MediaItem
mediaItem
;
...
@@ -1144,6 +1202,93 @@ import java.util.concurrent.CopyOnWriteArraySet;
...
@@ -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
(
private
Pair
<
Boolean
,
Integer
>
evaluateMediaItemTransitionReason
(
PlaybackInfo
playbackInfo
,
PlaybackInfo
playbackInfo
,
PlaybackInfo
oldPlaybackInfo
,
PlaybackInfo
oldPlaybackInfo
,
...
@@ -1169,7 +1314,7 @@ import java.util.concurrent.CopyOnWriteArraySet;
...
@@ -1169,7 +1314,7 @@ import java.util.concurrent.CopyOnWriteArraySet;
if
(!
oldWindowUid
.
equals
(
newWindowUid
))
{
if
(!
oldWindowUid
.
equals
(
newWindowUid
))
{
@Player
.
MediaItemTransitionReason
int
transitionReason
;
@Player
.
MediaItemTransitionReason
int
transitionReason
;
if
(
positionDiscontinuity
if
(
positionDiscontinuity
&&
positionDiscontinuityReason
==
DISCONTINUITY_REASON_
PERIOD
_TRANSITION
)
{
&&
positionDiscontinuityReason
==
DISCONTINUITY_REASON_
AUTO
_TRANSITION
)
{
transitionReason
=
MEDIA_ITEM_TRANSITION_REASON_AUTO
;
transitionReason
=
MEDIA_ITEM_TRANSITION_REASON_AUTO
;
}
else
if
(
positionDiscontinuity
}
else
if
(
positionDiscontinuity
&&
positionDiscontinuityReason
==
DISCONTINUITY_REASON_SEEK
)
{
&&
positionDiscontinuityReason
==
DISCONTINUITY_REASON_SEEK
)
{
...
@@ -1182,7 +1327,7 @@ import java.util.concurrent.CopyOnWriteArraySet;
...
@@ -1182,7 +1327,7 @@ import java.util.concurrent.CopyOnWriteArraySet;
}
}
return
new
Pair
<>(
/* isTransitioning */
true
,
transitionReason
);
return
new
Pair
<>(
/* isTransitioning */
true
,
transitionReason
);
}
else
if
(
positionDiscontinuity
}
else
if
(
positionDiscontinuity
&&
positionDiscontinuityReason
==
DISCONTINUITY_REASON_
PERIOD
_TRANSITION
&&
positionDiscontinuityReason
==
DISCONTINUITY_REASON_
AUTO
_TRANSITION
&&
newTimeline
.
getIndexOfPeriod
(
playbackInfo
.
periodId
.
periodUid
)
&&
newTimeline
.
getIndexOfPeriod
(
playbackInfo
.
periodId
.
periodUid
)
==
firstPeriodIndexInNewWindow
)
{
==
firstPeriodIndexInNewWindow
)
{
return
new
Pair
<>(
/* isTransitioning */
true
,
MEDIA_ITEM_TRANSITION_REASON_REPEAT
);
return
new
Pair
<>(
/* isTransitioning */
true
,
MEDIA_ITEM_TRANSITION_REASON_REPEAT
);
...
@@ -1245,13 +1390,18 @@ import java.util.concurrent.CopyOnWriteArraySet;
...
@@ -1245,13 +1390,18 @@ import java.util.concurrent.CopyOnWriteArraySet;
newPlaybackInfo
=
newPlaybackInfo
.
copyWithPlaybackState
(
maskingPlaybackState
);
newPlaybackInfo
=
newPlaybackInfo
.
copyWithPlaybackState
(
maskingPlaybackState
);
internalPlayer
.
setMediaSources
(
internalPlayer
.
setMediaSources
(
holders
,
startWindowIndex
,
C
.
msToUs
(
startPositionMs
),
shuffleOrder
);
holders
,
startWindowIndex
,
C
.
msToUs
(
startPositionMs
),
shuffleOrder
);
boolean
positionDiscontinuity
=
!
playbackInfo
.
periodId
.
periodUid
.
equals
(
newPlaybackInfo
.
periodId
.
periodUid
)
&&
!
playbackInfo
.
timeline
.
isEmpty
();
updatePlaybackInfo
(
updatePlaybackInfo
(
newPlaybackInfo
,
newPlaybackInfo
,
/* positionDiscontinuity= */
false
,
/* ignored */
Player
.
DISCONTINUITY_REASON_INTERNAL
,
/* timelineChangeReason= */
TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED
,
/* timelineChangeReason= */
TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED
,
/* ignored */
PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST
,
/* 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
(
private
List
<
MediaSourceList
.
MediaSourceHolder
>
addMediaSourceHolders
(
...
@@ -1319,11 +1469,13 @@ import java.util.concurrent.CopyOnWriteArraySet;
...
@@ -1319,11 +1469,13 @@ import java.util.concurrent.CopyOnWriteArraySet;
if
(
timeline
.
isEmpty
())
{
if
(
timeline
.
isEmpty
())
{
// Reset periodId and loadingPeriodId.
// Reset periodId and loadingPeriodId.
MediaPeriodId
dummyMediaPeriodId
=
PlaybackInfo
.
getDummyPeriodForEmptyTimeline
();
MediaPeriodId
dummyMediaPeriodId
=
PlaybackInfo
.
getDummyPeriodForEmptyTimeline
();
long
positionUs
=
C
.
msToUs
(
maskingWindowPositionMs
);
playbackInfo
=
playbackInfo
=
playbackInfo
.
copyWithNewPosition
(
playbackInfo
.
copyWithNewPosition
(
dummyMediaPeriodId
,
dummyMediaPeriodId
,
/* positionUs= */
C
.
msToUs
(
maskingWindowPositionMs
),
positionUs
,
/* requestedContentPositionUs= */
C
.
msToUs
(
maskingWindowPositionMs
),
/* requestedContentPositionUs= */
positionUs
,
/* discontinuityStartPositionUs= */
positionUs
,
/* totalBufferedDurationUs= */
0
,
/* totalBufferedDurationUs= */
0
,
TrackGroupArray
.
EMPTY
,
TrackGroupArray
.
EMPTY
,
emptyTrackSelectorResult
,
emptyTrackSelectorResult
,
...
@@ -1352,6 +1504,7 @@ import java.util.concurrent.CopyOnWriteArraySet;
...
@@ -1352,6 +1504,7 @@ import java.util.concurrent.CopyOnWriteArraySet;
newPeriodId
,
newPeriodId
,
/* positionUs= */
newContentPositionUs
,
/* positionUs= */
newContentPositionUs
,
/* requestedContentPositionUs= */
newContentPositionUs
,
/* requestedContentPositionUs= */
newContentPositionUs
,
/* discontinuityStartPositionUs= */
newContentPositionUs
,
/* totalBufferedDurationUs= */
0
,
/* totalBufferedDurationUs= */
0
,
playingPeriodChanged
?
TrackGroupArray
.
EMPTY
:
playbackInfo
.
trackGroups
,
playingPeriodChanged
?
TrackGroupArray
.
EMPTY
:
playbackInfo
.
trackGroups
,
playingPeriodChanged
?
emptyTrackSelectorResult
:
playbackInfo
.
trackSelectorResult
,
playingPeriodChanged
?
emptyTrackSelectorResult
:
playbackInfo
.
trackSelectorResult
,
...
@@ -1377,6 +1530,7 @@ import java.util.concurrent.CopyOnWriteArraySet;
...
@@ -1377,6 +1530,7 @@ import java.util.concurrent.CopyOnWriteArraySet;
newPeriodId
,
newPeriodId
,
/* positionUs= */
playbackInfo
.
positionUs
,
/* positionUs= */
playbackInfo
.
positionUs
,
/* requestedContentPositionUs= */
playbackInfo
.
positionUs
,
/* requestedContentPositionUs= */
playbackInfo
.
positionUs
,
playbackInfo
.
discontinuityStartPositionUs
,
/* totalBufferedDurationUs= */
maskedBufferedPositionUs
-
playbackInfo
.
positionUs
,
/* totalBufferedDurationUs= */
maskedBufferedPositionUs
-
playbackInfo
.
positionUs
,
playbackInfo
.
trackGroups
,
playbackInfo
.
trackGroups
,
playbackInfo
.
trackSelectorResult
,
playbackInfo
.
trackSelectorResult
,
...
@@ -1400,6 +1554,7 @@ import java.util.concurrent.CopyOnWriteArraySet;
...
@@ -1400,6 +1554,7 @@ import java.util.concurrent.CopyOnWriteArraySet;
newPeriodId
,
newPeriodId
,
/* positionUs= */
newContentPositionUs
,
/* positionUs= */
newContentPositionUs
,
/* requestedContentPositionUs= */
newContentPositionUs
,
/* requestedContentPositionUs= */
newContentPositionUs
,
/* discontinuityStartPositionUs= */
newContentPositionUs
,
maskedTotalBufferedDurationUs
,
maskedTotalBufferedDurationUs
,
playbackInfo
.
trackGroups
,
playbackInfo
.
trackGroups
,
playbackInfo
.
trackSelectorResult
,
playbackInfo
.
trackSelectorResult
,
...
@@ -1468,11 +1623,11 @@ import java.util.concurrent.CopyOnWriteArraySet;
...
@@ -1468,11 +1623,11 @@ import java.util.concurrent.CopyOnWriteArraySet;
return
timeline
.
getPeriodPosition
(
window
,
period
,
windowIndex
,
C
.
msToUs
(
windowPositionMs
));
return
timeline
.
getPeriodPosition
(
window
,
period
,
windowIndex
,
C
.
msToUs
(
windowPositionMs
));
}
}
private
long
periodPositionUsToWindowPosition
Ms
(
MediaPeriodId
periodId
,
long
positionUs
)
{
private
long
periodPositionUsToWindowPosition
Us
(
long
positionMs
=
C
.
usToMs
(
positionUs
);
Timeline
timeline
,
MediaPeriodId
periodId
,
long
positionUs
)
{
playbackInfo
.
timeline
.
getPeriodByUid
(
periodId
.
periodUid
,
period
);
timeline
.
getPeriodByUid
(
periodId
.
periodUid
,
period
);
position
Ms
+=
period
.
getPositionInWindowM
s
();
position
Us
+=
period
.
getPositionInWindowU
s
();
return
position
M
s
;
return
position
U
s
;
}
}
private
static
boolean
isPlaying
(
PlaybackInfo
playbackInfo
)
{
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;
...
@@ -668,7 +668,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
mediaSourceList
.
setMediaSources
(
mediaSourceList
.
setMediaSources
(
mediaSourceListUpdateMessage
.
mediaSourceHolders
,
mediaSourceListUpdateMessage
.
mediaSourceHolders
,
mediaSourceListUpdateMessage
.
shuffleOrder
);
mediaSourceListUpdateMessage
.
shuffleOrder
);
handleMediaSourceListInfoRefreshed
(
timeline
);
handleMediaSourceListInfoRefreshed
(
timeline
,
/* isSourceRefresh= */
false
);
}
}
private
void
addMediaItemsInternal
(
MediaSourceListUpdateMessage
addMessage
,
int
insertionIndex
)
private
void
addMediaItemsInternal
(
MediaSourceListUpdateMessage
addMessage
,
int
insertionIndex
)
...
@@ -679,7 +679,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
...
@@ -679,7 +679,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
insertionIndex
==
C
.
INDEX_UNSET
?
mediaSourceList
.
getSize
()
:
insertionIndex
,
insertionIndex
==
C
.
INDEX_UNSET
?
mediaSourceList
.
getSize
()
:
insertionIndex
,
addMessage
.
mediaSourceHolders
,
addMessage
.
mediaSourceHolders
,
addMessage
.
shuffleOrder
);
addMessage
.
shuffleOrder
);
handleMediaSourceListInfoRefreshed
(
timeline
);
handleMediaSourceListInfoRefreshed
(
timeline
,
/* isSourceRefresh= */
false
);
}
}
private
void
moveMediaItemsInternal
(
MoveMediaItemsMessage
moveMediaItemsMessage
)
private
void
moveMediaItemsInternal
(
MoveMediaItemsMessage
moveMediaItemsMessage
)
...
@@ -691,24 +691,25 @@ import java.util.concurrent.atomic.AtomicBoolean;
...
@@ -691,24 +691,25 @@ import java.util.concurrent.atomic.AtomicBoolean;
moveMediaItemsMessage
.
toIndex
,
moveMediaItemsMessage
.
toIndex
,
moveMediaItemsMessage
.
newFromIndex
,
moveMediaItemsMessage
.
newFromIndex
,
moveMediaItemsMessage
.
shuffleOrder
);
moveMediaItemsMessage
.
shuffleOrder
);
handleMediaSourceListInfoRefreshed
(
timeline
);
handleMediaSourceListInfoRefreshed
(
timeline
,
/* isSourceRefresh= */
false
);
}
}
private
void
removeMediaItemsInternal
(
int
fromIndex
,
int
toIndex
,
ShuffleOrder
shuffleOrder
)
private
void
removeMediaItemsInternal
(
int
fromIndex
,
int
toIndex
,
ShuffleOrder
shuffleOrder
)
throws
ExoPlaybackException
{
throws
ExoPlaybackException
{
playbackInfoUpdate
.
incrementPendingOperationAcks
(
/* operationAcks= */
1
);
playbackInfoUpdate
.
incrementPendingOperationAcks
(
/* operationAcks= */
1
);
Timeline
timeline
=
mediaSourceList
.
removeMediaSourceRange
(
fromIndex
,
toIndex
,
shuffleOrder
);
Timeline
timeline
=
mediaSourceList
.
removeMediaSourceRange
(
fromIndex
,
toIndex
,
shuffleOrder
);
handleMediaSourceListInfoRefreshed
(
timeline
);
handleMediaSourceListInfoRefreshed
(
timeline
,
/* isSourceRefresh= */
false
);
}
}
private
void
mediaSourceListUpdateRequestedInternal
()
throws
ExoPlaybackException
{
private
void
mediaSourceListUpdateRequestedInternal
()
throws
ExoPlaybackException
{
handleMediaSourceListInfoRefreshed
(
mediaSourceList
.
createTimeline
());
handleMediaSourceListInfoRefreshed
(
mediaSourceList
.
createTimeline
(),
/* isSourceRefresh= */
true
);
}
}
private
void
setShuffleOrderInternal
(
ShuffleOrder
shuffleOrder
)
throws
ExoPlaybackException
{
private
void
setShuffleOrderInternal
(
ShuffleOrder
shuffleOrder
)
throws
ExoPlaybackException
{
playbackInfoUpdate
.
incrementPendingOperationAcks
(
/* operationAcks= */
1
);
playbackInfoUpdate
.
incrementPendingOperationAcks
(
/* operationAcks= */
1
);
Timeline
timeline
=
mediaSourceList
.
setShuffleOrder
(
shuffleOrder
);
Timeline
timeline
=
mediaSourceList
.
setShuffleOrder
(
shuffleOrder
);
handleMediaSourceListInfoRefreshed
(
timeline
);
handleMediaSourceListInfoRefreshed
(
timeline
,
/* isSourceRefresh= */
false
);
}
}
private
void
notifyTrackSelectionPlayWhenReadyChanged
(
boolean
playWhenReady
)
{
private
void
notifyTrackSelectionPlayWhenReadyChanged
(
boolean
playWhenReady
)
{
...
@@ -803,10 +804,12 @@ import java.util.concurrent.atomic.AtomicBoolean;
...
@@ -803,10 +804,12 @@ import java.util.concurrent.atomic.AtomicBoolean;
if
(
newPositionUs
!=
playbackInfo
.
positionUs
)
{
if
(
newPositionUs
!=
playbackInfo
.
positionUs
)
{
playbackInfo
=
playbackInfo
=
handlePositionDiscontinuity
(
handlePositionDiscontinuity
(
periodId
,
newPositionUs
,
playbackInfo
.
requestedContentPositionUs
);
periodId
,
if
(
sendDiscontinuity
)
{
newPositionUs
,
playbackInfoUpdate
.
setPositionDiscontinuity
(
Player
.
DISCONTINUITY_REASON_INTERNAL
);
playbackInfo
.
requestedContentPositionUs
,
}
playbackInfo
.
discontinuityStartPositionUs
,
sendDiscontinuity
,
Player
.
DISCONTINUITY_REASON_INTERNAL
);
}
}
}
}
...
@@ -852,9 +855,11 @@ import java.util.concurrent.atomic.AtomicBoolean;
...
@@ -852,9 +855,11 @@ import java.util.concurrent.atomic.AtomicBoolean;
playbackInfo
=
playbackInfo
=
handlePositionDiscontinuity
(
handlePositionDiscontinuity
(
playbackInfo
.
periodId
,
playbackInfo
.
periodId
,
discontinuityPositionUs
,
/* positionUs= */
discontinuityPositionUs
,
playbackInfo
.
requestedContentPositionUs
);
playbackInfo
.
requestedContentPositionUs
,
playbackInfoUpdate
.
setPositionDiscontinuity
(
Player
.
DISCONTINUITY_REASON_INTERNAL
);
/* discontinuityStartPositionUs= */
discontinuityPositionUs
,
/* reportDiscontinuity= */
true
,
Player
.
DISCONTINUITY_REASON_INTERNAL
);
}
}
}
else
{
}
else
{
rendererPositionUs
=
rendererPositionUs
=
...
@@ -1166,10 +1171,13 @@ import java.util.concurrent.atomic.AtomicBoolean;
...
@@ -1166,10 +1171,13 @@ import java.util.concurrent.atomic.AtomicBoolean;
}
}
}
finally
{
}
finally
{
playbackInfo
=
playbackInfo
=
handlePositionDiscontinuity
(
periodId
,
periodPositionUs
,
requestedContentPositionUs
);
handlePositionDiscontinuity
(
if
(
seekPositionAdjusted
)
{
periodId
,
playbackInfoUpdate
.
setPositionDiscontinuity
(
Player
.
DISCONTINUITY_REASON_SEEK_ADJUSTMENT
);
periodPositionUs
,
}
requestedContentPositionUs
,
/* discontinuityStartPositionUs= */
periodPositionUs
,
/* reportDiscontinuity= */
seekPositionAdjusted
,
Player
.
DISCONTINUITY_REASON_SEEK_ADJUSTMENT
);
}
}
}
}
...
@@ -1385,6 +1393,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
...
@@ -1385,6 +1393,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
playbackInfo
.
timeline
,
playbackInfo
.
timeline
,
mediaPeriodId
,
mediaPeriodId
,
requestedContentPositionUs
,
requestedContentPositionUs
,
/* discontinuityStartPositionUs= */
startPositionUs
,
playbackInfo
.
playbackState
,
playbackInfo
.
playbackState
,
resetError
?
null
:
playbackInfo
.
playbackError
,
resetError
?
null
:
playbackInfo
.
playbackError
,
/* isLoading= */
false
,
/* isLoading= */
false
,
...
@@ -1395,9 +1404,9 @@ import java.util.concurrent.atomic.AtomicBoolean;
...
@@ -1395,9 +1404,9 @@ import java.util.concurrent.atomic.AtomicBoolean;
playbackInfo
.
playWhenReady
,
playbackInfo
.
playWhenReady
,
playbackInfo
.
playbackSuppressionReason
,
playbackInfo
.
playbackSuppressionReason
,
playbackInfo
.
playbackParameters
,
playbackInfo
.
playbackParameters
,
startPositionUs
,
/* bufferedPositionUs= */
startPositionUs
,
/* totalBufferedDurationUs= */
0
,
/* totalBufferedDurationUs= */
0
,
startPositionUs
,
/* positionUs= */
startPositionUs
,
offloadSchedulingEnabled
,
offloadSchedulingEnabled
,
/* sleepingForOffload= */
false
);
/* sleepingForOffload= */
false
);
if
(
releaseMediaSourceList
)
{
if
(
releaseMediaSourceList
)
{
...
@@ -1634,12 +1643,18 @@ import java.util.concurrent.atomic.AtomicBoolean;
...
@@ -1634,12 +1643,18 @@ import java.util.concurrent.atomic.AtomicBoolean;
long
periodPositionUs
=
long
periodPositionUs
=
playingPeriodHolder
.
applyTrackSelection
(
playingPeriodHolder
.
applyTrackSelection
(
newTrackSelectorResult
,
playbackInfo
.
positionUs
,
recreateStreams
,
streamResetFlags
);
newTrackSelectorResult
,
playbackInfo
.
positionUs
,
recreateStreams
,
streamResetFlags
);
boolean
hasDiscontinuity
=
playbackInfo
.
playbackState
!=
Player
.
STATE_ENDED
&&
periodPositionUs
!=
playbackInfo
.
positionUs
;
playbackInfo
=
playbackInfo
=
handlePositionDiscontinuity
(
handlePositionDiscontinuity
(
playbackInfo
.
periodId
,
periodPositionUs
,
playbackInfo
.
requestedContentPositionUs
);
playbackInfo
.
periodId
,
if
(
playbackInfo
.
playbackState
!=
Player
.
STATE_ENDED
periodPositionUs
,
&&
periodPositionUs
!=
playbackInfo
.
positionUs
)
{
playbackInfo
.
requestedContentPositionUs
,
playbackInfoUpdate
.
setPositionDiscontinuity
(
Player
.
DISCONTINUITY_REASON_INTERNAL
);
playbackInfo
.
discontinuityStartPositionUs
,
hasDiscontinuity
,
Player
.
DISCONTINUITY_REASON_INTERNAL
);
if
(
hasDiscontinuity
)
{
resetRendererPosition
(
periodPositionUs
);
resetRendererPosition
(
periodPositionUs
);
}
}
...
@@ -1742,7 +1757,8 @@ import java.util.concurrent.atomic.AtomicBoolean;
...
@@ -1742,7 +1757,8 @@ import java.util.concurrent.atomic.AtomicBoolean;
||
!
shouldPlayWhenReady
());
||
!
shouldPlayWhenReady
());
}
}
private
void
handleMediaSourceListInfoRefreshed
(
Timeline
timeline
)
throws
ExoPlaybackException
{
private
void
handleMediaSourceListInfoRefreshed
(
Timeline
timeline
,
boolean
isSourceRefresh
)
throws
ExoPlaybackException
{
PositionUpdateForPlaylistChange
positionUpdate
=
PositionUpdateForPlaylistChange
positionUpdate
=
resolvePositionForPlaylistChange
(
resolvePositionForPlaylistChange
(
timeline
,
timeline
,
...
@@ -1759,7 +1775,6 @@ import java.util.concurrent.atomic.AtomicBoolean;
...
@@ -1759,7 +1775,6 @@ import java.util.concurrent.atomic.AtomicBoolean;
long
newPositionUs
=
positionUpdate
.
periodPositionUs
;
long
newPositionUs
=
positionUpdate
.
periodPositionUs
;
boolean
periodPositionChanged
=
boolean
periodPositionChanged
=
!
playbackInfo
.
periodId
.
equals
(
newPeriodId
)
||
newPositionUs
!=
playbackInfo
.
positionUs
;
!
playbackInfo
.
periodId
.
equals
(
newPeriodId
)
||
newPositionUs
!=
playbackInfo
.
positionUs
;
try
{
try
{
if
(
positionUpdate
.
endPlayback
)
{
if
(
positionUpdate
.
endPlayback
)
{
if
(
playbackInfo
.
playbackState
!=
Player
.
STATE_IDLE
)
{
if
(
playbackInfo
.
playbackState
!=
Player
.
STATE_IDLE
)
{
...
@@ -1800,8 +1815,25 @@ import java.util.concurrent.atomic.AtomicBoolean;
...
@@ -1800,8 +1815,25 @@ import java.util.concurrent.atomic.AtomicBoolean;
:
C
.
TIME_UNSET
);
:
C
.
TIME_UNSET
);
if
(
periodPositionChanged
if
(
periodPositionChanged
||
newRequestedContentPositionUs
!=
playbackInfo
.
requestedContentPositionUs
)
{
||
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
=
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
();
resetPendingPauseAtEndOfPeriod
();
resolvePendingMessagePositions
(
resolvePendingMessagePositions
(
...
@@ -2049,12 +2081,10 @@ import java.util.concurrent.atomic.AtomicBoolean;
...
@@ -2049,12 +2081,10 @@ import java.util.concurrent.atomic.AtomicBoolean;
handlePositionDiscontinuity
(
handlePositionDiscontinuity
(
newPlayingPeriodHolder
.
info
.
id
,
newPlayingPeriodHolder
.
info
.
id
,
newPlayingPeriodHolder
.
info
.
startPositionUs
,
newPlayingPeriodHolder
.
info
.
startPositionUs
,
newPlayingPeriodHolder
.
info
.
requestedContentPositionUs
);
newPlayingPeriodHolder
.
info
.
requestedContentPositionUs
,
int
discontinuityReason
=
/* discontinuityStartPositionUs= */
newPlayingPeriodHolder
.
info
.
startPositionUs
,
oldPlayingPeriodHolder
.
info
.
isLastInTimelinePeriod
/* reportDiscontinuity= */
true
,
?
Player
.
DISCONTINUITY_REASON_PERIOD_TRANSITION
Player
.
DISCONTINUITY_REASON_AUTO_TRANSITION
);
:
Player
.
DISCONTINUITY_REASON_AD_INSERTION
;
playbackInfoUpdate
.
setPositionDiscontinuity
(
discontinuityReason
);
updateLivePlaybackSpeedControl
(
updateLivePlaybackSpeedControl
(
/* newTimeline= */
playbackInfo
.
timeline
,
/* newTimeline= */
playbackInfo
.
timeline
,
/* newPeriodId= */
newPlayingPeriodHolder
.
info
.
id
,
/* newPeriodId= */
newPlayingPeriodHolder
.
info
.
id
,
...
@@ -2140,7 +2170,10 @@ import java.util.concurrent.atomic.AtomicBoolean;
...
@@ -2140,7 +2170,10 @@ import java.util.concurrent.atomic.AtomicBoolean;
handlePositionDiscontinuity
(
handlePositionDiscontinuity
(
playbackInfo
.
periodId
,
playbackInfo
.
periodId
,
loadingPeriodHolder
.
info
.
startPositionUs
,
loadingPeriodHolder
.
info
.
startPositionUs
,
playbackInfo
.
requestedContentPositionUs
);
playbackInfo
.
requestedContentPositionUs
,
loadingPeriodHolder
.
info
.
startPositionUs
,
/* reportDiscontinuity= */
false
,
/* ignored */
Player
.
DISCONTINUITY_REASON_INTERNAL
);
}
}
maybeContinueLoading
();
maybeContinueLoading
();
}
}
...
@@ -2232,7 +2265,12 @@ import java.util.concurrent.atomic.AtomicBoolean;
...
@@ -2232,7 +2265,12 @@ import java.util.concurrent.atomic.AtomicBoolean;
@CheckResult
@CheckResult
private
PlaybackInfo
handlePositionDiscontinuity
(
private
PlaybackInfo
handlePositionDiscontinuity
(
MediaPeriodId
mediaPeriodId
,
long
positionUs
,
long
contentPositionUs
)
{
MediaPeriodId
mediaPeriodId
,
long
positionUs
,
long
contentPositionUs
,
long
discontinuityStartPositionUs
,
boolean
reportDiscontinuity
,
@DiscontinuityReason
int
discontinuityReason
)
{
deliverPendingMessageAtStartPositionRequired
=
deliverPendingMessageAtStartPositionRequired
=
deliverPendingMessageAtStartPositionRequired
deliverPendingMessageAtStartPositionRequired
||
positionUs
!=
playbackInfo
.
positionUs
||
positionUs
!=
playbackInfo
.
positionUs
...
@@ -2264,11 +2302,14 @@ import java.util.concurrent.atomic.AtomicBoolean;
...
@@ -2264,11 +2302,14 @@ import java.util.concurrent.atomic.AtomicBoolean;
trackSelectorResult
=
emptyTrackSelectorResult
;
trackSelectorResult
=
emptyTrackSelectorResult
;
staticMetadata
=
ImmutableList
.
of
();
staticMetadata
=
ImmutableList
.
of
();
}
}
if
(
reportDiscontinuity
)
{
playbackInfoUpdate
.
setPositionDiscontinuity
(
discontinuityReason
);
}
return
playbackInfo
.
copyWithNewPosition
(
return
playbackInfo
.
copyWithNewPosition
(
mediaPeriodId
,
mediaPeriodId
,
positionUs
,
positionUs
,
contentPositionUs
,
contentPositionUs
,
discontinuityStartPositionUs
,
getTotalBufferedDurationUs
(),
getTotalBufferedDurationUs
(),
trackGroupArray
,
trackGroupArray
,
trackSelectorResult
,
trackSelectorResult
,
...
...
library/core/src/main/java/com/google/android/exoplayer2/PlaybackInfo.java
View file @
dc4148d5
...
@@ -50,6 +50,8 @@ import java.util.List;
...
@@ -50,6 +50,8 @@ import java.util.List;
* suspended content.
* suspended content.
*/
*/
public
final
long
requestedContentPositionUs
;
public
final
long
requestedContentPositionUs
;
/** The start position after a reported position discontinuity, in microseconds. */
public
final
long
discontinuityStartPositionUs
;
/** The current playback state. One of the {@link Player}.STATE_ constants. */
/** The current playback state. One of the {@link Player}.STATE_ constants. */
@Player
.
State
public
final
int
playbackState
;
@Player
.
State
public
final
int
playbackState
;
/** The current playback error, or null if this is not an error state. */
/** The current playback error, or null if this is not an error state. */
...
@@ -104,6 +106,7 @@ import java.util.List;
...
@@ -104,6 +106,7 @@ import java.util.List;
Timeline
.
EMPTY
,
Timeline
.
EMPTY
,
PLACEHOLDER_MEDIA_PERIOD_ID
,
PLACEHOLDER_MEDIA_PERIOD_ID
,
/* requestedContentPositionUs= */
C
.
TIME_UNSET
,
/* requestedContentPositionUs= */
C
.
TIME_UNSET
,
/* discontinuityStartPositionUs= */
0
,
Player
.
STATE_IDLE
,
Player
.
STATE_IDLE
,
/* playbackError= */
null
,
/* playbackError= */
null
,
/* isLoading= */
false
,
/* isLoading= */
false
,
...
@@ -147,6 +150,7 @@ import java.util.List;
...
@@ -147,6 +150,7 @@ import java.util.List;
Timeline
timeline
,
Timeline
timeline
,
MediaPeriodId
periodId
,
MediaPeriodId
periodId
,
long
requestedContentPositionUs
,
long
requestedContentPositionUs
,
long
discontinuityStartPositionUs
,
@Player
.
State
int
playbackState
,
@Player
.
State
int
playbackState
,
@Nullable
ExoPlaybackException
playbackError
,
@Nullable
ExoPlaybackException
playbackError
,
boolean
isLoading
,
boolean
isLoading
,
...
@@ -165,6 +169,7 @@ import java.util.List;
...
@@ -165,6 +169,7 @@ import java.util.List;
this
.
timeline
=
timeline
;
this
.
timeline
=
timeline
;
this
.
periodId
=
periodId
;
this
.
periodId
=
periodId
;
this
.
requestedContentPositionUs
=
requestedContentPositionUs
;
this
.
requestedContentPositionUs
=
requestedContentPositionUs
;
this
.
discontinuityStartPositionUs
=
discontinuityStartPositionUs
;
this
.
playbackState
=
playbackState
;
this
.
playbackState
=
playbackState
;
this
.
playbackError
=
playbackError
;
this
.
playbackError
=
playbackError
;
this
.
isLoading
=
isLoading
;
this
.
isLoading
=
isLoading
;
...
@@ -207,6 +212,7 @@ import java.util.List;
...
@@ -207,6 +212,7 @@ import java.util.List;
MediaPeriodId
periodId
,
MediaPeriodId
periodId
,
long
positionUs
,
long
positionUs
,
long
requestedContentPositionUs
,
long
requestedContentPositionUs
,
long
discontinuityStartPositionUs
,
long
totalBufferedDurationUs
,
long
totalBufferedDurationUs
,
TrackGroupArray
trackGroups
,
TrackGroupArray
trackGroups
,
TrackSelectorResult
trackSelectorResult
,
TrackSelectorResult
trackSelectorResult
,
...
@@ -215,6 +221,7 @@ import java.util.List;
...
@@ -215,6 +221,7 @@ import java.util.List;
timeline
,
timeline
,
periodId
,
periodId
,
requestedContentPositionUs
,
requestedContentPositionUs
,
discontinuityStartPositionUs
,
playbackState
,
playbackState
,
playbackError
,
playbackError
,
isLoading
,
isLoading
,
...
@@ -244,6 +251,7 @@ import java.util.List;
...
@@ -244,6 +251,7 @@ import java.util.List;
timeline
,
timeline
,
periodId
,
periodId
,
requestedContentPositionUs
,
requestedContentPositionUs
,
discontinuityStartPositionUs
,
playbackState
,
playbackState
,
playbackError
,
playbackError
,
isLoading
,
isLoading
,
...
@@ -273,6 +281,7 @@ import java.util.List;
...
@@ -273,6 +281,7 @@ import java.util.List;
timeline
,
timeline
,
periodId
,
periodId
,
requestedContentPositionUs
,
requestedContentPositionUs
,
discontinuityStartPositionUs
,
playbackState
,
playbackState
,
playbackError
,
playbackError
,
isLoading
,
isLoading
,
...
@@ -302,6 +311,7 @@ import java.util.List;
...
@@ -302,6 +311,7 @@ import java.util.List;
timeline
,
timeline
,
periodId
,
periodId
,
requestedContentPositionUs
,
requestedContentPositionUs
,
discontinuityStartPositionUs
,
playbackState
,
playbackState
,
playbackError
,
playbackError
,
isLoading
,
isLoading
,
...
@@ -331,6 +341,7 @@ import java.util.List;
...
@@ -331,6 +341,7 @@ import java.util.List;
timeline
,
timeline
,
periodId
,
periodId
,
requestedContentPositionUs
,
requestedContentPositionUs
,
discontinuityStartPositionUs
,
playbackState
,
playbackState
,
playbackError
,
playbackError
,
isLoading
,
isLoading
,
...
@@ -360,6 +371,7 @@ import java.util.List;
...
@@ -360,6 +371,7 @@ import java.util.List;
timeline
,
timeline
,
periodId
,
periodId
,
requestedContentPositionUs
,
requestedContentPositionUs
,
discontinuityStartPositionUs
,
playbackState
,
playbackState
,
playbackError
,
playbackError
,
isLoading
,
isLoading
,
...
@@ -393,6 +405,7 @@ import java.util.List;
...
@@ -393,6 +405,7 @@ import java.util.List;
timeline
,
timeline
,
periodId
,
periodId
,
requestedContentPositionUs
,
requestedContentPositionUs
,
discontinuityStartPositionUs
,
playbackState
,
playbackState
,
playbackError
,
playbackError
,
isLoading
,
isLoading
,
...
@@ -422,6 +435,7 @@ import java.util.List;
...
@@ -422,6 +435,7 @@ import java.util.List;
timeline
,
timeline
,
periodId
,
periodId
,
requestedContentPositionUs
,
requestedContentPositionUs
,
discontinuityStartPositionUs
,
playbackState
,
playbackState
,
playbackError
,
playbackError
,
isLoading
,
isLoading
,
...
@@ -452,6 +466,7 @@ import java.util.List;
...
@@ -452,6 +466,7 @@ import java.util.List;
timeline
,
timeline
,
periodId
,
periodId
,
requestedContentPositionUs
,
requestedContentPositionUs
,
discontinuityStartPositionUs
,
playbackState
,
playbackState
,
playbackError
,
playbackError
,
isLoading
,
isLoading
,
...
@@ -481,6 +496,7 @@ import java.util.List;
...
@@ -481,6 +496,7 @@ import java.util.List;
timeline
,
timeline
,
periodId
,
periodId
,
requestedContentPositionUs
,
requestedContentPositionUs
,
discontinuityStartPositionUs
,
playbackState
,
playbackState
,
playbackError
,
playbackError
,
isLoading
,
isLoading
,
...
...
library/core/src/main/java/com/google/android/exoplayer2/analytics/AnalyticsCollector.java
View file @
dc4148d5
...
@@ -706,8 +706,13 @@ public class AnalyticsCollector
...
@@ -706,8 +706,13 @@ public class AnalyticsCollector
listener
->
listener
.
onPlayerError
(
eventTime
,
error
));
listener
->
listener
.
onPlayerError
(
eventTime
,
error
));
}
}
// Calling deprecated callback.
@SuppressWarnings
(
"deprecation"
)
@Override
@Override
public
final
void
onPositionDiscontinuity
(
@Player
.
DiscontinuityReason
int
reason
)
{
public
final
void
onPositionDiscontinuity
(
Player
.
PositionInfo
oldPosition
,
Player
.
PositionInfo
newPosition
,
@Player
.
DiscontinuityReason
int
reason
)
{
if
(
reason
==
Player
.
DISCONTINUITY_REASON_SEEK
)
{
if
(
reason
==
Player
.
DISCONTINUITY_REASON_SEEK
)
{
isSeeking
=
false
;
isSeeking
=
false
;
}
}
...
@@ -716,7 +721,10 @@ public class AnalyticsCollector
...
@@ -716,7 +721,10 @@ public class AnalyticsCollector
sendEvent
(
sendEvent
(
eventTime
,
eventTime
,
AnalyticsListener
.
EVENT_POSITION_DISCONTINUITY
,
AnalyticsListener
.
EVENT_POSITION_DISCONTINUITY
,
listener
->
listener
.
onPositionDiscontinuity
(
eventTime
,
reason
));
listener
->
{
listener
.
onPositionDiscontinuity
(
eventTime
,
reason
);
listener
.
onPositionDiscontinuity
(
eventTime
,
oldPosition
,
newPosition
,
reason
);
});
}
}
@Override
@Override
...
...
library/core/src/main/java/com/google/android/exoplayer2/analytics/AnalyticsListener.java
View file @
dc4148d5
...
@@ -236,7 +236,7 @@ public interface AnalyticsListener {
...
@@ -236,7 +236,7 @@ public interface AnalyticsListener {
int
EVENT_PLAYER_ERROR
=
Player
.
EVENT_PLAYER_ERROR
;
int
EVENT_PLAYER_ERROR
=
Player
.
EVENT_PLAYER_ERROR
;
/**
/**
* A position discontinuity occurred. See {@link
* A position discontinuity occurred. See {@link
* Player.EventListener#onPositionDiscontinuity(int)}.
* Player.EventListener#onPositionDiscontinuity(
Player.PositionInfo, Player.PositionInfo,
int)}.
*/
*/
int
EVENT_POSITION_DISCONTINUITY
=
Player
.
EVENT_POSITION_DISCONTINUITY
;
int
EVENT_POSITION_DISCONTINUITY
=
Player
.
EVENT_POSITION_DISCONTINUITY
;
/** {@link Player#getPlaybackParameters()} changed. */
/** {@link Player#getPlaybackParameters()} changed. */
...
@@ -532,12 +532,25 @@ public interface AnalyticsListener {
...
@@ -532,12 +532,25 @@ public interface AnalyticsListener {
@Player
.
MediaItemTransitionReason
int
reason
)
{}
@Player
.
MediaItemTransitionReason
int
reason
)
{}
/**
/**
* @deprecated Use {@link #onPositionDiscontinuity(EventTime, Player.PositionInfo,
* Player.PositionInfo, int)} instead.
*/
@Deprecated
default
void
onPositionDiscontinuity
(
EventTime
eventTime
,
@DiscontinuityReason
int
reason
)
{}
/**
* Called when a position discontinuity occurred.
* Called when a position discontinuity occurred.
*
*
* @param eventTime The event time.
* @param eventTime The event time.
* @param oldPosition The position before the discontinuity.
* @param newPosition The position after the discontinuity.
* @param reason The reason for the position discontinuity.
* @param reason The reason for the position discontinuity.
*/
*/
default
void
onPositionDiscontinuity
(
EventTime
eventTime
,
@DiscontinuityReason
int
reason
)
{}
default
void
onPositionDiscontinuity
(
EventTime
eventTime
,
Player
.
PositionInfo
oldPosition
,
Player
.
PositionInfo
newPosition
,
@DiscontinuityReason
int
reason
)
{}
/**
/**
* Called when a seek operation started.
* Called when a seek operation started.
...
...
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
...
@@ -191,9 +191,7 @@ public final class DefaultPlaybackSessionManager implements PlaybackSessionManag
public
synchronized
void
updateSessionsWithDiscontinuity
(
public
synchronized
void
updateSessionsWithDiscontinuity
(
EventTime
eventTime
,
@DiscontinuityReason
int
reason
)
{
EventTime
eventTime
,
@DiscontinuityReason
int
reason
)
{
Assertions
.
checkNotNull
(
listener
);
Assertions
.
checkNotNull
(
listener
);
boolean
hasAutomaticTransition
=
boolean
hasAutomaticTransition
=
reason
==
Player
.
DISCONTINUITY_REASON_AUTO_TRANSITION
;
reason
==
Player
.
DISCONTINUITY_REASON_PERIOD_TRANSITION
||
reason
==
Player
.
DISCONTINUITY_REASON_AD_INSERTION
;
Iterator
<
SessionDescriptor
>
iterator
=
sessions
.
values
().
iterator
();
Iterator
<
SessionDescriptor
>
iterator
=
sessions
.
values
().
iterator
();
while
(
iterator
.
hasNext
())
{
while
(
iterator
.
hasNext
())
{
SessionDescriptor
session
=
iterator
.
next
();
SessionDescriptor
session
=
iterator
.
next
();
...
...
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 {
...
@@ -89,7 +89,10 @@ public class DebugTextViewHelper implements Player.EventListener, Runnable {
}
}
@Override
@Override
public
final
void
onPositionDiscontinuity
(
@Player
.
DiscontinuityReason
int
reason
)
{
public
final
void
onPositionDiscontinuity
(
Player
.
PositionInfo
oldPosition
,
Player
.
PositionInfo
newPosition
,
@Player
.
DiscontinuityReason
int
reason
)
{
updateAndPost
();
updateAndPost
();
}
}
...
...
library/core/src/main/java/com/google/android/exoplayer2/util/EventLogger.java
View file @
dc4148d5
...
@@ -141,8 +141,50 @@ public class EventLogger implements AnalyticsListener {
...
@@ -141,8 +141,50 @@ public class EventLogger implements AnalyticsListener {
}
}
@Override
@Override
public
void
onPositionDiscontinuity
(
EventTime
eventTime
,
@Player
.
DiscontinuityReason
int
reason
)
{
public
void
onPositionDiscontinuity
(
logd
(
eventTime
,
"positionDiscontinuity"
,
getDiscontinuityReasonString
(
reason
));
EventTime
eventTime
,
Player
.
PositionInfo
oldPosition
,
Player
.
PositionInfo
newPosition
,
@Player
.
DiscontinuityReason
int
reason
)
{
StringBuilder
builder
=
new
StringBuilder
();
builder
.
append
(
"reason="
)
.
append
(
getDiscontinuityReasonString
(
reason
))
.
append
(
", PositionInfo:old ["
)
.
append
(
"window="
)
.
append
(
oldPosition
.
windowIndex
)
.
append
(
", period="
)
.
append
(
oldPosition
.
periodIndex
)
.
append
(
", pos="
)
.
append
(
oldPosition
.
positionMs
);
if
(
oldPosition
.
adGroupIndex
!=
C
.
INDEX_UNSET
)
{
builder
.
append
(
", contentPos="
)
.
append
(
oldPosition
.
contentPositionMs
)
.
append
(
", adGroup="
)
.
append
(
oldPosition
.
adGroupIndex
)
.
append
(
", ad="
)
.
append
(
oldPosition
.
adIndexInAdGroup
);
}
builder
.
append
(
"], PositionInfo:new ["
)
.
append
(
"window="
)
.
append
(
newPosition
.
windowIndex
)
.
append
(
", period="
)
.
append
(
newPosition
.
periodIndex
)
.
append
(
", pos="
)
.
append
(
newPosition
.
positionMs
);
if
(
newPosition
.
adGroupIndex
!=
C
.
INDEX_UNSET
)
{
builder
.
append
(
", contentPos="
)
.
append
(
newPosition
.
contentPositionMs
)
.
append
(
", adGroup="
)
.
append
(
newPosition
.
adGroupIndex
)
.
append
(
", ad="
)
.
append
(
newPosition
.
adIndexInAdGroup
);
}
builder
.
append
(
"]"
);
logd
(
eventTime
,
"positionDiscontinuity"
,
builder
.
toString
());
}
}
@Override
@Override
...
@@ -658,14 +700,16 @@ public class EventLogger implements AnalyticsListener {
...
@@ -658,14 +700,16 @@ public class EventLogger implements AnalyticsListener {
private
static
String
getDiscontinuityReasonString
(
@Player
.
DiscontinuityReason
int
reason
)
{
private
static
String
getDiscontinuityReasonString
(
@Player
.
DiscontinuityReason
int
reason
)
{
switch
(
reason
)
{
switch
(
reason
)
{
case
Player
.
DISCONTINUITY_REASON_
PERIOD
_TRANSITION
:
case
Player
.
DISCONTINUITY_REASON_
AUTO
_TRANSITION
:
return
"
PERIOD
_TRANSITION"
;
return
"
AUTO
_TRANSITION"
;
case
Player
.
DISCONTINUITY_REASON_SEEK
:
case
Player
.
DISCONTINUITY_REASON_SEEK
:
return
"SEEK"
;
return
"SEEK"
;
case
Player
.
DISCONTINUITY_REASON_SEEK_ADJUSTMENT
:
case
Player
.
DISCONTINUITY_REASON_SEEK_ADJUSTMENT
:
return
"SEEK_ADJUSTMENT"
;
return
"SEEK_ADJUSTMENT"
;
case
Player
.
DISCONTINUITY_REASON_AD_INSERTION
:
case
Player
.
DISCONTINUITY_REASON_REMOVE
:
return
"AD_INSERTION"
;
return
"REMOVE"
;
case
Player
.
DISCONTINUITY_REASON_SKIP
:
return
"SKIP"
;
case
Player
.
DISCONTINUITY_REASON_INTERNAL
:
case
Player
.
DISCONTINUITY_REASON_INTERNAL
:
return
"INTERNAL"
;
return
"INTERNAL"
;
default
:
default
:
...
...
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;
...
@@ -49,6 +49,7 @@ import static com.google.common.truth.Truth.assertThat;
import
static
org
.
junit
.
Assert
.
assertArrayEquals
;
import
static
org
.
junit
.
Assert
.
assertArrayEquals
;
import
static
org
.
junit
.
Assert
.
assertThrows
;
import
static
org
.
junit
.
Assert
.
assertThrows
;
import
static
org
.
junit
.
Assert
.
fail
;
import
static
org
.
junit
.
Assert
.
fail
;
import
static
org
.
mockito
.
AdditionalMatchers
.
not
;
import
static
org
.
mockito
.
ArgumentMatchers
.
any
;
import
static
org
.
mockito
.
ArgumentMatchers
.
any
;
import
static
org
.
mockito
.
ArgumentMatchers
.
anyBoolean
;
import
static
org
.
mockito
.
ArgumentMatchers
.
anyBoolean
;
import
static
org
.
mockito
.
ArgumentMatchers
.
anyInt
;
import
static
org
.
mockito
.
ArgumentMatchers
.
anyInt
;
...
@@ -132,6 +133,7 @@ import com.google.android.exoplayer2.util.Clock;
...
@@ -132,6 +133,7 @@ import com.google.android.exoplayer2.util.Clock;
import
com.google.android.exoplayer2.util.MimeTypes
;
import
com.google.android.exoplayer2.util.MimeTypes
;
import
com.google.common.collect.ImmutableList
;
import
com.google.common.collect.ImmutableList
;
import
com.google.common.collect.Iterables
;
import
com.google.common.collect.Iterables
;
import
com.google.common.collect.Lists
;
import
com.google.common.collect.Range
;
import
com.google.common.collect.Range
;
import
java.io.IOException
;
import
java.io.IOException
;
import
java.util.ArrayList
;
import
java.util.ArrayList
;
...
@@ -210,6 +212,7 @@ public final class ExoPlayerTest {
...
@@ -210,6 +212,7 @@ public final class ExoPlayerTest {
.
onTimelineChanged
(
.
onTimelineChanged
(
argThat
(
noUid
(
timeline
)),
eq
(
Player
.
TIMELINE_CHANGE_REASON_SOURCE_UPDATE
));
argThat
(
noUid
(
timeline
)),
eq
(
Player
.
TIMELINE_CHANGE_REASON_SOURCE_UPDATE
));
inOrder
.
verify
(
mockListener
,
never
()).
onPositionDiscontinuity
(
anyInt
());
inOrder
.
verify
(
mockListener
,
never
()).
onPositionDiscontinuity
(
anyInt
());
inOrder
.
verify
(
mockListener
,
never
()).
onPositionDiscontinuity
(
any
(),
any
(),
anyInt
());
assertThat
(
renderer
.
getFormatsRead
()).
isEmpty
();
assertThat
(
renderer
.
getFormatsRead
()).
isEmpty
();
assertThat
(
renderer
.
sampleBufferReadCount
).
isEqualTo
(
0
);
assertThat
(
renderer
.
sampleBufferReadCount
).
isEqualTo
(
0
);
assertThat
(
renderer
.
isEnded
).
isFalse
();
assertThat
(
renderer
.
isEnded
).
isFalse
();
...
@@ -244,6 +247,7 @@ public final class ExoPlayerTest {
...
@@ -244,6 +247,7 @@ public final class ExoPlayerTest {
.
onTracksChanged
(
.
onTracksChanged
(
eq
(
new
TrackGroupArray
(
new
TrackGroup
(
ExoPlayerTestRunner
.
VIDEO_FORMAT
))),
any
());
eq
(
new
TrackGroupArray
(
new
TrackGroup
(
ExoPlayerTestRunner
.
VIDEO_FORMAT
))),
any
());
inOrder
.
verify
(
mockListener
,
never
()).
onPositionDiscontinuity
(
anyInt
());
inOrder
.
verify
(
mockListener
,
never
()).
onPositionDiscontinuity
(
anyInt
());
inOrder
.
verify
(
mockListener
,
never
()).
onPositionDiscontinuity
(
any
(),
any
(),
anyInt
());
assertThat
(
renderer
.
getFormatsRead
()).
containsExactly
(
ExoPlayerTestRunner
.
VIDEO_FORMAT
);
assertThat
(
renderer
.
getFormatsRead
()).
containsExactly
(
ExoPlayerTestRunner
.
VIDEO_FORMAT
);
assertThat
(
renderer
.
sampleBufferReadCount
).
isEqualTo
(
1
);
assertThat
(
renderer
.
sampleBufferReadCount
).
isEqualTo
(
1
);
assertThat
(
renderer
.
isEnded
).
isTrue
();
assertThat
(
renderer
.
isEnded
).
isTrue
();
...
@@ -268,14 +272,14 @@ public final class ExoPlayerTest {
...
@@ -268,14 +272,14 @@ public final class ExoPlayerTest {
.
verify
(
mockEventListener
)
.
verify
(
mockEventListener
)
.
onTimelineChanged
(
.
onTimelineChanged
(
argThat
(
noUid
(
new
FakeMediaSource
.
InitialTimeline
(
timeline
))),
argThat
(
noUid
(
new
FakeMediaSource
.
InitialTimeline
(
timeline
))),
eq
(
Player
.
DISCONTINUITY_REASON_
PERIOD
_TRANSITION
));
eq
(
Player
.
DISCONTINUITY_REASON_
AUTO
_TRANSITION
));
inOrder
inOrder
.
verify
(
mockEventListener
)
.
verify
(
mockEventListener
)
.
onTimelineChanged
(
.
onTimelineChanged
(
argThat
(
noUid
(
timeline
)),
eq
(
Player
.
TIMELINE_CHANGE_REASON_SOURCE_UPDATE
));
argThat
(
noUid
(
timeline
)),
eq
(
Player
.
TIMELINE_CHANGE_REASON_SOURCE_UPDATE
));
inOrder
inOrder
.
verify
(
mockEventListener
,
times
(
2
))
.
verify
(
mockEventListener
,
times
(
2
))
.
onPositionDiscontinuity
(
Player
.
DISCONTINUITY_REASON_PERIOD_TRANSITION
);
.
onPositionDiscontinuity
(
any
(),
any
(),
eq
(
Player
.
DISCONTINUITY_REASON_AUTO_TRANSITION
)
);
assertThat
(
renderer
.
getFormatsRead
())
assertThat
(
renderer
.
getFormatsRead
())
.
containsExactly
(
.
containsExactly
(
ExoPlayerTestRunner
.
VIDEO_FORMAT
,
ExoPlayerTestRunner
.
VIDEO_FORMAT
,
...
@@ -313,7 +317,7 @@ public final class ExoPlayerTest {
...
@@ -313,7 +317,7 @@ public final class ExoPlayerTest {
argThat
(
noUid
(
timeline
)),
eq
(
Player
.
TIMELINE_CHANGE_REASON_SOURCE_UPDATE
));
argThat
(
noUid
(
timeline
)),
eq
(
Player
.
TIMELINE_CHANGE_REASON_SOURCE_UPDATE
));
inOrder
inOrder
.
verify
(
mockEventListener
,
times
(
99
))
.
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
.
getFormatsRead
()).
hasSize
(
100
);
assertThat
(
renderer
.
sampleBufferReadCount
).
isEqualTo
(
100
);
assertThat
(
renderer
.
sampleBufferReadCount
).
isEqualTo
(
100
);
assertThat
(
renderer
.
isEnded
).
isTrue
();
assertThat
(
renderer
.
isEnded
).
isTrue
();
...
@@ -397,7 +401,7 @@ public final class ExoPlayerTest {
...
@@ -397,7 +401,7 @@ public final class ExoPlayerTest {
argThat
(
noUid
(
timeline
)),
eq
(
Player
.
TIMELINE_CHANGE_REASON_SOURCE_UPDATE
));
argThat
(
noUid
(
timeline
)),
eq
(
Player
.
TIMELINE_CHANGE_REASON_SOURCE_UPDATE
));
inOrder
inOrder
.
verify
(
mockEventListener
,
times
(
2
))
.
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
(
audioRenderer
.
positionResetCount
).
isEqualTo
(
1
);
assertThat
(
videoRenderer
.
isEnded
).
isTrue
();
assertThat
(
videoRenderer
.
isEnded
).
isTrue
();
assertThat
(
audioRenderer
.
isEnded
).
isTrue
();
assertThat
(
audioRenderer
.
isEnded
).
isTrue
();
...
@@ -441,7 +445,6 @@ public final class ExoPlayerTest {
...
@@ -441,7 +445,6 @@ public final class ExoPlayerTest {
// second source was suppressed as we replace it with the third source before the update
// second source was suppressed as we replace it with the third source before the update
// arrives.
// arrives.
InOrder
inOrder
=
inOrder
(
mockEventListener
);
InOrder
inOrder
=
inOrder
(
mockEventListener
);
inOrder
.
verify
(
mockEventListener
,
never
()).
onPositionDiscontinuity
(
anyInt
());
inOrder
inOrder
.
verify
(
mockEventListener
)
.
verify
(
mockEventListener
)
.
onTimelineChanged
(
.
onTimelineChanged
(
...
@@ -452,12 +455,23 @@ public final class ExoPlayerTest {
...
@@ -452,12 +455,23 @@ public final class ExoPlayerTest {
.
onTimelineChanged
(
.
onTimelineChanged
(
argThat
(
noUid
(
firstTimeline
)),
eq
(
Player
.
TIMELINE_CHANGE_REASON_SOURCE_UPDATE
));
argThat
(
noUid
(
firstTimeline
)),
eq
(
Player
.
TIMELINE_CHANGE_REASON_SOURCE_UPDATE
));
inOrder
inOrder
.
verify
(
mockEventListener
,
times
(
2
)
)
.
verify
(
mockEventListener
)
.
onTimelineChanged
(
.
onTimelineChanged
(
argThat
(
noUid
(
placeholderTimeline
)),
argThat
(
noUid
(
placeholderTimeline
)),
eq
(
Player
.
TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED
));
eq
(
Player
.
TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED
));
inOrder
inOrder
.
verify
(
mockEventListener
)
.
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
(
.
onTimelineChanged
(
argThat
(
noUid
(
thirdTimeline
)),
eq
(
Player
.
TIMELINE_CHANGE_REASON_SOURCE_UPDATE
));
argThat
(
noUid
(
thirdTimeline
)),
eq
(
Player
.
TIMELINE_CHANGE_REASON_SOURCE_UPDATE
));
inOrder
inOrder
...
@@ -542,11 +556,11 @@ public final class ExoPlayerTest {
...
@@ -542,11 +556,11 @@ public final class ExoPlayerTest {
.
blockUntilEnded
(
TIMEOUT_MS
);
.
blockUntilEnded
(
TIMEOUT_MS
);
testRunner
.
assertPlayedPeriodIndices
(
0
,
1
,
0
,
2
,
1
,
2
);
testRunner
.
assertPlayedPeriodIndices
(
0
,
1
,
0
,
2
,
1
,
2
);
testRunner
.
assertPositionDiscontinuityReasonsEqual
(
testRunner
.
assertPositionDiscontinuityReasonsEqual
(
Player
.
DISCONTINUITY_REASON_
PERIOD
_TRANSITION
,
Player
.
DISCONTINUITY_REASON_
AUTO
_TRANSITION
,
Player
.
DISCONTINUITY_REASON_
PERIOD
_TRANSITION
,
Player
.
DISCONTINUITY_REASON_
AUTO
_TRANSITION
,
Player
.
DISCONTINUITY_REASON_
PERIOD
_TRANSITION
,
Player
.
DISCONTINUITY_REASON_
AUTO
_TRANSITION
,
Player
.
DISCONTINUITY_REASON_
PERIOD
_TRANSITION
,
Player
.
DISCONTINUITY_REASON_
AUTO
_TRANSITION
,
Player
.
DISCONTINUITY_REASON_
PERIOD
_TRANSITION
);
Player
.
DISCONTINUITY_REASON_
AUTO
_TRANSITION
);
assertThat
(
renderer
.
isEnded
).
isTrue
();
assertThat
(
renderer
.
isEnded
).
isTrue
();
}
}
...
@@ -603,7 +617,7 @@ public final class ExoPlayerTest {
...
@@ -603,7 +617,7 @@ public final class ExoPlayerTest {
.
start
()
.
start
()
.
blockUntilEnded
(
TIMEOUT_MS
);
.
blockUntilEnded
(
TIMEOUT_MS
);
// There is still one discontinuity from content to content for the failed ad insertion.
// 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
@Test
...
@@ -1267,7 +1281,8 @@ public final class ExoPlayerTest {
...
@@ -1267,7 +1281,8 @@ public final class ExoPlayerTest {
Player
.
TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED
,
Player
.
TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED
,
Player
.
TIMELINE_CHANGE_REASON_SOURCE_UPDATE
,
Player
.
TIMELINE_CHANGE_REASON_SOURCE_UPDATE
,
Player
.
TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED
);
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
(
currentWindowIndex
[
0
]).
isEqualTo
(
1
);
assertThat
(
currentPosition
[
0
]).
isGreaterThan
(
0
);
assertThat
(
currentPosition
[
0
]).
isGreaterThan
(
0
);
...
@@ -2616,8 +2631,7 @@ public final class ExoPlayerTest {
...
@@ -2616,8 +2631,7 @@ public final class ExoPlayerTest {
@Test
@Test
public
void
timelineUpdateWithNewMidrollAdCuePoint_dropsPrebufferedPeriod
()
throws
Exception
{
public
void
timelineUpdateWithNewMidrollAdCuePoint_dropsPrebufferedPeriod
()
throws
Exception
{
Timeline
timeline1
=
Timeline
timeline1
=
new
FakeTimeline
(
TimelineWindowDefinition
.
createPlaceholder
(
/* tag= */
0
));
new
FakeTimeline
(
new
TimelineWindowDefinition
(
/* periodCount= */
1
,
/* id= */
0
));
AdPlaybackState
adPlaybackStateWithMidroll
=
AdPlaybackState
adPlaybackStateWithMidroll
=
FakeTimeline
.
createAdPlaybackState
(
FakeTimeline
.
createAdPlaybackState
(
/* adsPerAdGroup= */
1
,
/* adsPerAdGroup= */
1
,
...
@@ -2653,9 +2667,10 @@ public final class ExoPlayerTest {
...
@@ -2653,9 +2667,10 @@ public final class ExoPlayerTest {
testRunner
.
assertTimelineChangeReasonsEqual
(
testRunner
.
assertTimelineChangeReasonsEqual
(
Player
.
TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED
,
Player
.
TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED
,
Player
.
TIMELINE_CHANGE_REASON_SOURCE_UPDATE
,
Player
.
TIMELINE_CHANGE_REASON_SOURCE_UPDATE
);
Player
.
TIMELINE_CHANGE_REASON_SOURCE_UPDATE
);
testRunner
.
assertPlayedPeriodIndices
(
0
);
testRunner
.
assertPlayedPeriodIndices
(
0
);
testRunner
.
assertPositionDiscontinuityReasonsEqual
(
Player
.
DISCONTINUITY_REASON_AUTO_TRANSITION
,
Player
.
DISCONTINUITY_REASON_AUTO_TRANSITION
);
assertThat
(
mediaSource
.
getCreatedMediaPeriods
()).
hasSize
(
4
);
assertThat
(
mediaSource
.
getCreatedMediaPeriods
()).
hasSize
(
4
);
assertThat
(
mediaSource
.
getCreatedMediaPeriods
().
get
(
0
).
nextAdGroupIndex
)
assertThat
(
mediaSource
.
getCreatedMediaPeriods
().
get
(
0
).
nextAdGroupIndex
)
.
isEqualTo
(
C
.
INDEX_UNSET
);
.
isEqualTo
(
C
.
INDEX_UNSET
);
...
@@ -2718,7 +2733,7 @@ public final class ExoPlayerTest {
...
@@ -2718,7 +2733,7 @@ public final class ExoPlayerTest {
// When the ad finishes, the player position should be at or after the requested seek position.
// When the ad finishes, the player position should be at or after the requested seek position.
TestPlayerRunHelper
.
runUntilPositionDiscontinuity
(
TestPlayerRunHelper
.
runUntilPositionDiscontinuity
(
player
,
Player
.
DISCONTINUITY_REASON_A
D_INSER
TION
);
player
,
Player
.
DISCONTINUITY_REASON_A
UTO_TRANSI
TION
);
assertThat
(
player
.
getCurrentPosition
()).
isAtLeast
(
seekPositionMs
);
assertThat
(
player
.
getCurrentPosition
()).
isAtLeast
(
seekPositionMs
);
}
}
...
@@ -3019,7 +3034,7 @@ public final class ExoPlayerTest {
...
@@ -3019,7 +3034,7 @@ public final class ExoPlayerTest {
@Override
@Override
public
void
onPositionDiscontinuity
(
@DiscontinuityReason
int
reason
)
{
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
());
positionAtDiscontinuityMs
.
set
(
playerReference
.
get
().
getCurrentPosition
());
clockAtDiscontinuityMs
.
set
(
clock
.
elapsedRealtime
());
clockAtDiscontinuityMs
.
set
(
clock
.
elapsedRealtime
());
}
}
...
@@ -3242,7 +3257,7 @@ public final class ExoPlayerTest {
...
@@ -3242,7 +3257,7 @@ public final class ExoPlayerTest {
new
EventListener
()
{
new
EventListener
()
{
@Override
@Override
public
void
onPositionDiscontinuity
(
@DiscontinuityReason
int
reason
)
{
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
)
{
if
(
bufferedPositionAtFirstDiscontinuityMs
.
get
()
==
C
.
TIME_UNSET
)
{
bufferedPositionAtFirstDiscontinuityMs
.
set
(
bufferedPositionAtFirstDiscontinuityMs
.
set
(
playerReference
.
get
().
getBufferedPosition
());
playerReference
.
get
().
getBufferedPosition
());
...
@@ -3296,7 +3311,7 @@ public final class ExoPlayerTest {
...
@@ -3296,7 +3311,7 @@ public final class ExoPlayerTest {
new
EventListener
()
{
new
EventListener
()
{
@Override
@Override
public
void
onPositionDiscontinuity
(
@DiscontinuityReason
int
reason
)
{
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
());
contentStartPositionMs
.
set
(
playerReference
.
get
().
getContentPosition
());
}
}
}
}
...
@@ -3349,7 +3364,7 @@ public final class ExoPlayerTest {
...
@@ -3349,7 +3364,7 @@ public final class ExoPlayerTest {
new
EventListener
()
{
new
EventListener
()
{
@Override
@Override
public
void
onPositionDiscontinuity
(
@DiscontinuityReason
int
reason
)
{
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
());
contentStartPositionMs
.
set
(
playerReference
.
get
().
getContentPosition
());
}
}
}
}
...
@@ -7584,7 +7599,7 @@ public final class ExoPlayerTest {
...
@@ -7584,7 +7599,7 @@ public final class ExoPlayerTest {
firstMediaSource
.
setNewSourceInfo
(
timelineWithOffsets
);
firstMediaSource
.
setNewSourceInfo
(
timelineWithOffsets
);
// Wait until player transitions to second source (which also has non-zero offsets).
// Wait until player transitions to second source (which also has non-zero offsets).
TestPlayerRunHelper
.
runUntilPositionDiscontinuity
(
TestPlayerRunHelper
.
runUntilPositionDiscontinuity
(
player
,
Player
.
DISCONTINUITY_REASON_
PERIOD
_TRANSITION
);
player
,
Player
.
DISCONTINUITY_REASON_
AUTO
_TRANSITION
);
assertThat
(
player
.
getCurrentWindowIndex
()).
isEqualTo
(
1
);
assertThat
(
player
.
getCurrentWindowIndex
()).
isEqualTo
(
1
);
player
.
release
();
player
.
release
();
...
@@ -7680,7 +7695,7 @@ public final class ExoPlayerTest {
...
@@ -7680,7 +7695,7 @@ public final class ExoPlayerTest {
@Override
@Override
public
void
onPositionDiscontinuity
(
int
reason
)
{
public
void
onPositionDiscontinuity
(
int
reason
)
{
if
(
reason
==
Player
.
DISCONTINUITY_REASON_SEEK
if
(
reason
==
Player
.
DISCONTINUITY_REASON_SEEK
||
reason
==
Player
.
DISCONTINUITY_REASON_
PERIOD
_TRANSITION
)
{
||
reason
==
Player
.
DISCONTINUITY_REASON_
AUTO
_TRANSITION
)
{
currentMediaItems
.
add
(
playerHolder
[
0
].
getCurrentMediaItem
());
currentMediaItems
.
add
(
playerHolder
[
0
].
getCurrentMediaItem
());
}
}
}
}
...
@@ -9271,7 +9286,7 @@ public final class ExoPlayerTest {
...
@@ -9271,7 +9286,7 @@ public final class ExoPlayerTest {
verify
(
listener
).
onTimelineChanged
(
any
(),
anyInt
());
verify
(
listener
).
onTimelineChanged
(
any
(),
anyInt
());
verify
(
listener
).
onMediaItemTransition
(
any
(),
anyInt
());
verify
(
listener
).
onMediaItemTransition
(
any
(),
anyInt
());
verify
(
listener
).
onPositionDiscontinuity
(
anyInt
());
verify
(
listener
).
onPositionDiscontinuity
(
any
(),
any
(),
any
Int
());
verify
(
listener
).
onPlaybackParametersChanged
(
any
());
verify
(
listener
).
onPlaybackParametersChanged
(
any
());
ArgumentCaptor
<
Player
.
Events
>
eventCaptor
=
ArgumentCaptor
.
forClass
(
Player
.
Events
.
class
);
ArgumentCaptor
<
Player
.
Events
>
eventCaptor
=
ArgumentCaptor
.
forClass
(
Player
.
Events
.
class
);
verify
(
listener
).
onEvents
(
eq
(
player
),
eventCaptor
.
capture
());
verify
(
listener
).
onEvents
(
eq
(
player
),
eventCaptor
.
capture
());
...
@@ -9312,7 +9327,7 @@ public final class ExoPlayerTest {
...
@@ -9312,7 +9327,7 @@ public final class ExoPlayerTest {
// Verify that all callbacks have been called at least once.
// Verify that all callbacks have been called at least once.
verify
(
listener
,
atLeastOnce
()).
onTimelineChanged
(
any
(),
anyInt
());
verify
(
listener
,
atLeastOnce
()).
onTimelineChanged
(
any
(),
anyInt
());
verify
(
listener
,
atLeastOnce
()).
onMediaItemTransition
(
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
()).
onPlaybackParametersChanged
(
any
());
verify
(
listener
,
atLeastOnce
()).
onRepeatModeChanged
(
anyInt
());
verify
(
listener
,
atLeastOnce
()).
onRepeatModeChanged
(
anyInt
());
verify
(
listener
,
atLeastOnce
()).
onShuffleModeEnabledChanged
(
anyBoolean
());
verify
(
listener
,
atLeastOnce
()).
onShuffleModeEnabledChanged
(
anyBoolean
());
...
@@ -9342,6 +9357,877 @@ public final class ExoPlayerTest {
...
@@ -9342,6 +9357,877 @@ public final class ExoPlayerTest {
assertThat
(
containsEvent
(
allEvents
,
Player
.
EVENT_PLAYER_ERROR
)).
isTrue
();
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.
// Internal methods.
private
static
ActionSchedule
.
Builder
addSurfaceSwitch
(
ActionSchedule
.
Builder
builder
)
{
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 {
...
@@ -430,6 +430,7 @@ public final class MediaPeriodQueueTest {
mediaPeriodQueue
.
resolveMediaPeriodIdForAds
(
mediaPeriodQueue
.
resolveMediaPeriodIdForAds
(
playlistTimeline
,
firstPeriodUid
,
/* positionUs= */
0
),
playlistTimeline
,
firstPeriodUid
,
/* positionUs= */
0
),
/* requestedContentPositionUs= */
C
.
TIME_UNSET
,
/* requestedContentPositionUs= */
C
.
TIME_UNSET
,
/* discontinuityStartPositionUs= */
0
,
Player
.
STATE_READY
,
Player
.
STATE_READY
,
/* playbackError= */
null
,
/* playbackError= */
null
,
/* isLoading= */
false
,
/* isLoading= */
false
,
...
...
library/core/src/test/java/com/google/android/exoplayer2/analytics/AnalyticsCollectorTest.java
View file @
dc4148d5
...
@@ -662,6 +662,8 @@ public final class AnalyticsCollectorTest {
...
@@ -662,6 +662,8 @@ public final class AnalyticsCollectorTest {
period0Seq0
/* SOURCE_UPDATE */
,
period0Seq0
/* SOURCE_UPDATE */
,
WINDOW_0
/* PLAYLIST_CHANGE */
,
WINDOW_0
/* PLAYLIST_CHANGE */
,
period0Seq1
/* SOURCE_UPDATE */
);
period0Seq1
/* SOURCE_UPDATE */
);
assertThat
(
listener
.
getEvents
(
EVENT_POSITION_DISCONTINUITY
))
.
containsExactly
(
WINDOW_0
/* REMOVE */
);
assertThat
(
listener
.
getEvents
(
EVENT_IS_LOADING_CHANGED
))
assertThat
(
listener
.
getEvents
(
EVENT_IS_LOADING_CHANGED
))
.
containsExactly
(
period0Seq0
,
period0Seq0
,
period0Seq1
,
period0Seq1
)
.
containsExactly
(
period0Seq0
,
period0Seq0
,
period0Seq1
,
period0Seq1
)
.
inOrder
();
.
inOrder
();
...
@@ -937,6 +939,9 @@ public final class AnalyticsCollectorTest {
...
@@ -937,6 +939,9 @@ public final class AnalyticsCollectorTest {
period0Seq0
/* SOURCE_UPDATE (second item) */
,
period0Seq0
/* SOURCE_UPDATE (second item) */
,
period0Seq1
/* PLAYLIST_CHANGED (remove) */
)
period0Seq1
/* PLAYLIST_CHANGED (remove) */
)
.
inOrder
();
.
inOrder
();
assertThat
(
listener
.
getEvents
(
EVENT_POSITION_DISCONTINUITY
))
.
containsExactly
(
period0Seq1
/* REMOVE */
)
.
inOrder
();
assertThat
(
listener
.
getEvents
(
EVENT_IS_LOADING_CHANGED
))
assertThat
(
listener
.
getEvents
(
EVENT_IS_LOADING_CHANGED
))
.
containsExactly
(
period0Seq0
,
period0Seq0
,
period0Seq0
,
period0Seq0
);
.
containsExactly
(
period0Seq0
,
period0Seq0
,
period0Seq0
,
period0Seq0
);
assertThat
(
listener
.
getEvents
(
EVENT_TRACKS_CHANGED
))
assertThat
(
listener
.
getEvents
(
EVENT_TRACKS_CHANGED
))
...
@@ -1037,9 +1042,11 @@ public final class AnalyticsCollectorTest {
...
@@ -1037,9 +1042,11 @@ public final class AnalyticsCollectorTest {
new
Player
.
EventListener
()
{
new
Player
.
EventListener
()
{
@Override
@Override
public
void
onPositionDiscontinuity
(
public
void
onPositionDiscontinuity
(
Player
.
PositionInfo
oldPosition
,
Player
.
PositionInfo
newPosition
,
@Player
.
DiscontinuityReason
int
reason
)
{
@Player
.
DiscontinuityReason
int
reason
)
{
if
(!
player
.
isPlayingAd
()
if
(!
player
.
isPlayingAd
()
&&
reason
==
Player
.
DISCONTINUITY_REASON_A
D_INSER
TION
)
{
&&
reason
==
Player
.
DISCONTINUITY_REASON_A
UTO_TRANSI
TION
)
{
// Finished playing ad. Marked as played.
// Finished playing ad. Marked as played.
adPlaybackState
.
set
(
adPlaybackState
.
set
(
adPlaybackState
adPlaybackState
...
@@ -1651,7 +1658,7 @@ public final class AnalyticsCollectorTest {
...
@@ -1651,7 +1658,7 @@ public final class AnalyticsCollectorTest {
player
.
addMediaSource
(
new
FakeMediaSource
(
new
FakeTimeline
(),
formats
));
player
.
addMediaSource
(
new
FakeMediaSource
(
new
FakeTimeline
(),
formats
));
player
.
play
();
player
.
play
();
TestPlayerRunHelper
.
runUntilPositionDiscontinuity
(
TestPlayerRunHelper
.
runUntilPositionDiscontinuity
(
player
,
Player
.
DISCONTINUITY_REASON_
PERIOD
_TRANSITION
);
player
,
Player
.
DISCONTINUITY_REASON_
AUTO
_TRANSITION
);
player
.
setMediaItem
(
MediaItem
.
fromUri
(
"http://this-will-throw-an-exception.mp4"
));
player
.
setMediaItem
(
MediaItem
.
fromUri
(
"http://this-will-throw-an-exception.mp4"
));
TestPlayerRunHelper
.
runUntilPlaybackState
(
player
,
Player
.
STATE_IDLE
);
TestPlayerRunHelper
.
runUntilPlaybackState
(
player
,
Player
.
STATE_IDLE
);
ShadowLooper
.
runMainLooperToNextTask
();
ShadowLooper
.
runMainLooperToNextTask
();
...
@@ -2085,7 +2092,11 @@ public final class AnalyticsCollectorTest {
...
@@ -2085,7 +2092,11 @@ public final class AnalyticsCollectorTest {
}
}
@Override
@Override
public
void
onPositionDiscontinuity
(
EventTime
eventTime
,
int
reason
)
{
public
void
onPositionDiscontinuity
(
EventTime
eventTime
,
Player
.
PositionInfo
oldPosition
,
Player
.
PositionInfo
newPosition
,
int
reason
)
{
reportedEvents
.
add
(
new
ReportedEvent
(
EVENT_POSITION_DISCONTINUITY
,
eventTime
));
reportedEvents
.
add
(
new
ReportedEvent
(
EVENT_POSITION_DISCONTINUITY
,
eventTime
));
}
}
...
...
library/core/src/test/java/com/google/android/exoplayer2/analytics/DefaultPlaybackSessionManagerTest.java
View file @
dc4148d5
...
@@ -461,9 +461,9 @@ public final class DefaultPlaybackSessionManagerTest {
...
@@ -461,9 +461,9 @@ public final class DefaultPlaybackSessionManagerTest {
sessionManager
.
updateSessionsWithTimelineChange
(
contentEventTime1
);
sessionManager
.
updateSessionsWithTimelineChange
(
contentEventTime1
);
sessionManager
.
updateSessions
(
adEventTime1
);
sessionManager
.
updateSessions
(
adEventTime1
);
sessionManager
.
updateSessionsWithDiscontinuity
(
sessionManager
.
updateSessionsWithDiscontinuity
(
adEventTime1
,
Player
.
DISCONTINUITY_REASON_A
D_INSER
TION
);
adEventTime1
,
Player
.
DISCONTINUITY_REASON_A
UTO_TRANSI
TION
);
sessionManager
.
updateSessionsWithDiscontinuity
(
sessionManager
.
updateSessionsWithDiscontinuity
(
contentEventTime2
,
Player
.
DISCONTINUITY_REASON_A
D_INSER
TION
);
contentEventTime2
,
Player
.
DISCONTINUITY_REASON_A
UTO_TRANSI
TION
);
String
adSessionId2
=
String
adSessionId2
=
sessionManager
.
getSessionForMediaPeriodId
(
adTimeline
,
adEventTime2
.
mediaPeriodId
);
sessionManager
.
getSessionForMediaPeriodId
(
adTimeline
,
adEventTime2
.
mediaPeriodId
);
...
@@ -751,7 +751,7 @@ public final class DefaultPlaybackSessionManagerTest {
...
@@ -751,7 +751,7 @@ public final class DefaultPlaybackSessionManagerTest {
sessionManager
.
updateSessions
(
eventTime2
);
sessionManager
.
updateSessions
(
eventTime2
);
sessionManager
.
updateSessionsWithDiscontinuity
(
sessionManager
.
updateSessionsWithDiscontinuity
(
eventTime2
,
Player
.
DISCONTINUITY_REASON_
PERIOD
_TRANSITION
);
eventTime2
,
Player
.
DISCONTINUITY_REASON_
AUTO
_TRANSITION
);
verify
(
mockListener
).
onSessionCreated
(
eq
(
eventTime1
),
anyString
());
verify
(
mockListener
).
onSessionCreated
(
eq
(
eventTime1
),
anyString
());
verify
(
mockListener
).
onSessionActive
(
eq
(
eventTime1
),
anyString
());
verify
(
mockListener
).
onSessionActive
(
eq
(
eventTime1
),
anyString
());
...
@@ -781,7 +781,7 @@ public final class DefaultPlaybackSessionManagerTest {
...
@@ -781,7 +781,7 @@ public final class DefaultPlaybackSessionManagerTest {
sessionManager
.
getSessionForMediaPeriodId
(
timeline
,
eventTime2
.
mediaPeriodId
);
sessionManager
.
getSessionForMediaPeriodId
(
timeline
,
eventTime2
.
mediaPeriodId
);
sessionManager
.
updateSessionsWithDiscontinuity
(
sessionManager
.
updateSessionsWithDiscontinuity
(
eventTime2
,
Player
.
DISCONTINUITY_REASON_
PERIOD
_TRANSITION
);
eventTime2
,
Player
.
DISCONTINUITY_REASON_
AUTO
_TRANSITION
);
verify
(
mockListener
).
onSessionCreated
(
eventTime1
,
sessionId1
);
verify
(
mockListener
).
onSessionCreated
(
eventTime1
,
sessionId1
);
verify
(
mockListener
).
onSessionActive
(
eventTime1
,
sessionId1
);
verify
(
mockListener
).
onSessionActive
(
eventTime1
,
sessionId1
);
...
@@ -960,7 +960,7 @@ public final class DefaultPlaybackSessionManagerTest {
...
@@ -960,7 +960,7 @@ public final class DefaultPlaybackSessionManagerTest {
adTimeline
,
contentEventTimeDuringPreroll
.
mediaPeriodId
);
adTimeline
,
contentEventTimeDuringPreroll
.
mediaPeriodId
);
sessionManager
.
updateSessionsWithDiscontinuity
(
sessionManager
.
updateSessionsWithDiscontinuity
(
contentEventTimeBetweenAds
,
Player
.
DISCONTINUITY_REASON_A
D_INSER
TION
);
contentEventTimeBetweenAds
,
Player
.
DISCONTINUITY_REASON_A
UTO_TRANSI
TION
);
InOrder
inOrder
=
inOrder
(
mockListener
);
InOrder
inOrder
=
inOrder
(
mockListener
);
inOrder
.
verify
(
mockListener
).
onSessionCreated
(
contentEventTimeDuringPreroll
,
contentSessionId
);
inOrder
.
verify
(
mockListener
).
onSessionCreated
(
contentEventTimeDuringPreroll
,
contentSessionId
);
...
@@ -1025,7 +1025,7 @@ public final class DefaultPlaybackSessionManagerTest {
...
@@ -1025,7 +1025,7 @@ public final class DefaultPlaybackSessionManagerTest {
sessionManager
.
updateSessions
(
adEventTime2
);
sessionManager
.
updateSessions
(
adEventTime2
);
sessionManager
.
updateSessionsWithDiscontinuity
(
sessionManager
.
updateSessionsWithDiscontinuity
(
adEventTime1
,
Player
.
DISCONTINUITY_REASON_A
D_INSER
TION
);
adEventTime1
,
Player
.
DISCONTINUITY_REASON_A
UTO_TRANSI
TION
);
verify
(
mockListener
,
never
()).
onSessionFinished
(
any
(),
anyString
(),
anyBoolean
());
verify
(
mockListener
,
never
()).
onSessionFinished
(
any
(),
anyString
(),
anyBoolean
());
}
}
...
@@ -1083,7 +1083,7 @@ public final class DefaultPlaybackSessionManagerTest {
...
@@ -1083,7 +1083,7 @@ public final class DefaultPlaybackSessionManagerTest {
sessionManager
.
getSessionForMediaPeriodId
(
adTimeline
,
adEventTime2
.
mediaPeriodId
);
sessionManager
.
getSessionForMediaPeriodId
(
adTimeline
,
adEventTime2
.
mediaPeriodId
);
sessionManager
.
updateSessionsWithDiscontinuity
(
sessionManager
.
updateSessionsWithDiscontinuity
(
adEventTime1
,
Player
.
DISCONTINUITY_REASON_A
D_INSER
TION
);
adEventTime1
,
Player
.
DISCONTINUITY_REASON_A
UTO_TRANSI
TION
);
sessionManager
.
updateSessionsWithDiscontinuity
(
adEventTime2
,
Player
.
DISCONTINUITY_REASON_SEEK
);
sessionManager
.
updateSessionsWithDiscontinuity
(
adEventTime2
,
Player
.
DISCONTINUITY_REASON_SEEK
);
verify
(
mockListener
).
onSessionCreated
(
eq
(
contentEventTime
),
anyString
());
verify
(
mockListener
).
onSessionCreated
(
eq
(
contentEventTime
),
anyString
());
...
...
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
...
@@ -1625,7 +1625,10 @@ public class PlayerView extends FrameLayout implements AdsLoader.AdViewProvider
}
}
@Override
@Override
public
void
onPositionDiscontinuity
(
@DiscontinuityReason
int
reason
)
{
public
void
onPositionDiscontinuity
(
Player
.
PositionInfo
oldPosition
,
Player
.
PositionInfo
newPosition
,
@DiscontinuityReason
int
reason
)
{
if
(
isPlayingAd
()
&&
controllerHideDuringAds
)
{
if
(
isPlayingAd
()
&&
controllerHideDuringAds
)
{
hideController
();
hideController
();
}
}
...
...
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
...
@@ -1647,7 +1647,10 @@ public class StyledPlayerView extends FrameLayout implements AdsLoader.AdViewPro
}
}
@Override
@Override
public
void
onPositionDiscontinuity
(
@DiscontinuityReason
int
reason
)
{
public
void
onPositionDiscontinuity
(
Player
.
PositionInfo
oldPosition
,
Player
.
PositionInfo
newPosition
,
@DiscontinuityReason
int
reason
)
{
if
(
isPlayingAd
()
&&
controllerHideDuringAds
)
{
if
(
isPlayingAd
()
&&
controllerHideDuringAds
)
{
hideController
();
hideController
();
}
}
...
...
robolectricutils/src/main/java/com/google/android/exoplayer2/robolectric/TestPlayerRunHelper.java
View file @
dc4148d5
...
@@ -158,8 +158,8 @@ public class TestPlayerRunHelper {
...
@@ -158,8 +158,8 @@ public class TestPlayerRunHelper {
/**
/**
* Runs tasks of the main {@link Looper} until a {@link
* Runs tasks of the main {@link Looper} until a {@link
* Player.EventListener#onPositionDiscontinuity
} callback with the specified {@link
* Player.EventListener#onPositionDiscontinuity
(Player.PositionInfo, Player.PositionInfo, int)}
* Player.DiscontinuityReason} occurred.
*
callback with the specified {@link
Player.DiscontinuityReason} occurred.
*
*
* @param player The {@link Player}.
* @param player The {@link Player}.
* @param expectedReason The expected {@link Player.DiscontinuityReason}.
* @param expectedReason The expected {@link Player.DiscontinuityReason}.
...
@@ -173,7 +173,8 @@ public class TestPlayerRunHelper {
...
@@ -173,7 +173,8 @@ public class TestPlayerRunHelper {
Player
.
EventListener
listener
=
Player
.
EventListener
listener
=
new
Player
.
EventListener
()
{
new
Player
.
EventListener
()
{
@Override
@Override
public
void
onPositionDiscontinuity
(
int
reason
)
{
public
void
onPositionDiscontinuity
(
Player
.
PositionInfo
oldPosition
,
Player
.
PositionInfo
newPosition
,
int
reason
)
{
if
(
reason
==
expectedReason
)
{
if
(
reason
==
expectedReason
)
{
receivedCallback
.
set
(
true
);
receivedCallback
.
set
(
true
);
player
.
removeListener
(
this
);
player
.
removeListener
(
this
);
...
...
testutils/src/main/java/com/google/android/exoplayer2/testutil/Action.java
View file @
dc4148d5
...
@@ -803,7 +803,10 @@ public abstract class Action {
...
@@ -803,7 +803,10 @@ public abstract class Action {
}
}
}
}
/** Waits for {@link Player.EventListener#onPositionDiscontinuity(int)}. */
/**
* Waits for {@link Player.EventListener#onPositionDiscontinuity(Player.PositionInfo,
* Player.PositionInfo, int)}.
*/
public
static
final
class
WaitForPositionDiscontinuity
extends
Action
{
public
static
final
class
WaitForPositionDiscontinuity
extends
Action
{
/** @param tag A tag to use for logging. */
/** @param tag A tag to use for logging. */
...
@@ -824,7 +827,10 @@ public abstract class Action {
...
@@ -824,7 +827,10 @@ public abstract class Action {
player
.
addListener
(
player
.
addListener
(
new
Player
.
EventListener
()
{
new
Player
.
EventListener
()
{
@Override
@Override
public
void
onPositionDiscontinuity
(
@Player
.
DiscontinuityReason
int
reason
)
{
public
void
onPositionDiscontinuity
(
Player
.
PositionInfo
oldPosition
,
Player
.
PositionInfo
newPosition
,
@Player
.
DiscontinuityReason
int
reason
)
{
player
.
removeListener
(
this
);
player
.
removeListener
(
this
);
nextAction
.
schedule
(
player
,
trackSelector
,
surface
,
handler
);
nextAction
.
schedule
(
player
,
trackSelector
,
surface
,
handler
);
}
}
...
...
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
...
@@ -575,7 +575,8 @@ public final class ExoPlayerTestRunner implements Player.EventListener, ActionSc
}
}
/**
/**
* Asserts that {@link Player.EventListener#onPositionDiscontinuity(int)} was not called.
* Asserts that {@link Player.EventListener#onPositionDiscontinuity(Player.PositionInfo,
* Player.PositionInfo, int)} was not called.
*/
*/
public
void
assertNoPositionDiscontinuities
()
{
public
void
assertNoPositionDiscontinuities
()
{
assertThat
(
discontinuityReasons
).
isEmpty
();
assertThat
(
discontinuityReasons
).
isEmpty
();
...
@@ -583,7 +584,8 @@ public final class ExoPlayerTestRunner implements Player.EventListener, ActionSc
...
@@ -583,7 +584,8 @@ public final class ExoPlayerTestRunner implements Player.EventListener, ActionSc
/**
/**
* Asserts that the discontinuity reasons reported by {@link
* Asserts that the discontinuity reasons reported by {@link
* Player.EventListener#onPositionDiscontinuity(int)} are equal to the provided values.
* Player.EventListener#onPositionDiscontinuity(Player.PositionInfo, Player.PositionInfo, int)}
* are equal to the provided values.
*
*
* @param discontinuityReasons The expected discontinuity reasons.
* @param discontinuityReasons The expected discontinuity reasons.
*/
*/
...
@@ -676,10 +678,15 @@ public final class ExoPlayerTestRunner implements Player.EventListener, ActionSc
...
@@ -676,10 +678,15 @@ public final class ExoPlayerTestRunner implements Player.EventListener, ActionSc
}
}
@Override
@Override
public
void
onPositionDiscontinuity
(
@Player
.
DiscontinuityReason
int
reason
)
{
public
void
onPositionDiscontinuity
(
Player
.
PositionInfo
oldPosition
,
Player
.
PositionInfo
newPosition
,
@Player
.
DiscontinuityReason
int
reason
)
{
discontinuityReasons
.
add
(
reason
);
discontinuityReasons
.
add
(
reason
);
int
currentIndex
=
player
.
getCurrentPeriodIndex
();
int
currentIndex
=
player
.
getCurrentPeriodIndex
();
if
(
reason
==
Player
.
DISCONTINUITY_REASON_PERIOD_TRANSITION
if
((
reason
==
Player
.
DISCONTINUITY_REASON_AUTO_TRANSITION
&&
oldPosition
.
adGroupIndex
!=
C
.
INDEX_UNSET
&&
newPosition
.
adGroupIndex
!=
C
.
INDEX_UNSET
)
||
periodIndices
.
isEmpty
()
||
periodIndices
.
isEmpty
()
||
periodIndices
.
get
(
periodIndices
.
size
()
-
1
)
!=
currentIndex
)
{
||
periodIndices
.
get
(
periodIndices
.
size
()
-
1
)
!=
currentIndex
)
{
// Ignore seek or internal discontinuities within a period.
// Ignore seek or internal discontinuities within a period.
...
...
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