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
05f6d248
authored
Nov 23, 2020
by
andrewlewis
Committed by
kim-vde
Nov 24, 2020
Browse files
Options
_('Browse Files')
Download
Email Patches
Plain Diff
Add support for ad playlists with `ImaAdsLoader`
Issue: #3750 PiperOrigin-RevId: 343878310
parent
689e89e5
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
626 additions
and
271 deletions
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/ima/src/main/java/com/google/android/exoplayer2/ext/ima/AdTagLoader.java
View file @
05f6d248
...
...
@@ -17,8 +17,8 @@ package com.google.android.exoplayer2.ext.ima;
import
static
com
.
google
.
android
.
exoplayer2
.
ext
.
ima
.
ImaUtil
.
BITRATE_UNSET
;
import
static
com
.
google
.
android
.
exoplayer2
.
ext
.
ima
.
ImaUtil
.
TIMEOUT_UNSET
;
import
static
com
.
google
.
android
.
exoplayer2
.
ext
.
ima
.
ImaUtil
.
getAdGroupTimesUsForCuePoints
;
import
static
com
.
google
.
android
.
exoplayer2
.
ext
.
ima
.
ImaUtil
.
getImaLooper
;
import
static
com
.
google
.
android
.
exoplayer2
.
util
.
Assertions
.
checkArgument
;
import
static
com
.
google
.
android
.
exoplayer2
.
util
.
Assertions
.
checkNotNull
;
import
static
com
.
google
.
android
.
exoplayer2
.
util
.
Assertions
.
checkState
;
import
static
java
.
lang
.
Math
.
max
;
...
...
@@ -280,11 +280,11 @@ import java.util.Map;
}
}
/**
Starts using the ads loader for playback. */
public
void
start
(
Player
player
,
AdViewProvider
adViewProvider
,
EventListener
eventListener
)
{
this
.
player
=
player
;
player
.
addListener
(
this
);
boolean
playWhenReady
=
player
.
getPlayWhenReady
();
/**
* Starts passing events from this instance (including any pending ad playback state) and
* registers obstructions.
*/
public
void
start
(
AdViewProvider
adViewProvider
,
EventListener
eventListener
)
{
this
.
eventListener
=
eventListener
;
lastVolumePercent
=
0
;
lastAdProgress
=
VideoProgressUpdate
.
VIDEO_TIME_NOT_READY
;
...
...
@@ -293,13 +293,9 @@ import java.util.Map;
if
(!
AdPlaybackState
.
NONE
.
equals
(
adPlaybackState
))
{
// Pass the ad playback state to the player, and resume ads if necessary.
eventListener
.
onAdPlaybackState
(
adPlaybackState
);
if
(
adsManager
!=
null
&&
imaPausedContent
&&
playWhenReady
)
{
adsManager
.
resume
();
}
}
else
if
(
adsManager
!=
null
)
{
adPlaybackState
=
new
AdPlaybackState
(
adsId
,
ImaUtil
.
getAdGroupTimesUsForCuePoints
(
adsManager
.
getAdCuePoints
()));
new
AdPlaybackState
(
adsId
,
getAdGroupTimesUsForCuePoints
(
adsManager
.
getAdCuePoints
()));
updateAdPlaybackState
();
}
for
(
OverlayInfo
overlayInfo
:
adViewProvider
.
getAdOverlayInfos
())
{
...
...
@@ -311,14 +307,36 @@ import java.util.Map;
}
}
/** Stops using the ads loader for playback. */
public
void
stop
()
{
@Nullable
Player
player
=
this
.
player
;
if
(
player
==
null
)
{
return
;
/**
* Populates the ad playback state with loaded cue points, if available. Any preroll will be
* paused immediately while waiting for this instance to be {@link #activate(Player) activated}.
*/
public
void
maybePreloadAds
(
long
contentPositionMs
,
long
contentDurationMs
)
{
maybeInitializeAdsManager
(
contentPositionMs
,
contentDurationMs
);
}
/** Activates playback. */
public
void
activate
(
Player
player
)
{
this
.
player
=
player
;
player
.
addListener
(
this
);
boolean
playWhenReady
=
player
.
getPlayWhenReady
();
onTimelineChanged
(
player
.
getCurrentTimeline
(),
Player
.
TIMELINE_CHANGE_REASON_SOURCE_UPDATE
);
if
(!
AdPlaybackState
.
NONE
.
equals
(
adPlaybackState
)
&&
adsManager
!=
null
&&
imaPausedContent
&&
playWhenReady
)
{
adsManager
.
resume
();
}
if
(
adsManager
!=
null
&&
imaPausedContent
)
{
adsManager
.
pause
();
}
/** Deactivates playback. */
public
void
deactivate
()
{
Player
player
=
checkNotNull
(
this
.
player
);
if
(!
AdPlaybackState
.
NONE
.
equals
(
adPlaybackState
)
&&
imaPausedContent
)
{
if
(
adsManager
!=
null
)
{
adsManager
.
pause
();
}
adPlaybackState
=
adPlaybackState
.
withAdResumePositionUs
(
playingAd
?
C
.
msToUs
(
player
.
getCurrentPosition
())
:
0
);
...
...
@@ -326,10 +344,15 @@ import java.util.Map;
lastVolumePercent
=
getPlayerVolumePercent
();
lastAdProgress
=
getAdVideoProgressUpdate
();
lastContentProgress
=
getContentVideoProgressUpdate
();
adDisplayContainer
.
unregisterAllFriendlyObstructions
();
player
.
removeListener
(
this
);
this
.
player
=
null
;
}
/** Stops passing of events from this instance and unregisters obstructions. */
public
void
stop
()
{
eventListener
=
null
;
adDisplayContainer
.
unregisterAllFriendlyObstructions
();
}
/** Releases all resources used by the ad tag loader. */
...
...
@@ -392,7 +415,6 @@ import java.util.Map;
// The player is being reset or contains no media.
return
;
}
checkArgument
(
timeline
.
getPeriodCount
()
==
1
);
this
.
timeline
=
timeline
;
Player
player
=
checkNotNull
(
this
.
player
);
long
contentDurationUs
=
timeline
.
getPeriod
(
player
.
getCurrentPeriodIndex
(),
period
).
durationUs
;
...
...
@@ -592,14 +614,13 @@ import java.util.Map;
}
private
VideoProgressUpdate
getContentVideoProgressUpdate
()
{
if
(
player
==
null
)
{
return
lastContentProgress
;
}
boolean
hasContentDuration
=
contentDurationMs
!=
C
.
TIME_UNSET
;
long
contentPositionMs
;
if
(
pendingContentPositionMs
!=
C
.
TIME_UNSET
)
{
sentPendingContentPositionMs
=
true
;
contentPositionMs
=
pendingContentPositionMs
;
}
else
if
(
player
==
null
)
{
return
lastContentProgress
;
}
else
if
(
fakeContentProgressElapsedRealtimeMs
!=
C
.
TIME_UNSET
)
{
long
elapsedSinceEndMs
=
SystemClock
.
elapsedRealtime
()
-
fakeContentProgressElapsedRealtimeMs
;
contentPositionMs
=
fakeContentProgressOffsetMs
+
elapsedSinceEndMs
;
...
...
@@ -923,7 +944,8 @@ import java.util.Map;
adCallbacks
.
get
(
i
).
onResume
(
adMediaInfo
);
}
}
if
(!
checkNotNull
(
player
).
getPlayWhenReady
())
{
if
(
player
==
null
||
!
player
.
getPlayWhenReady
())
{
// Either this loader hasn't been activated yet, or the player is paused now.
checkNotNull
(
adsManager
).
pause
();
}
}
...
...
@@ -941,7 +963,14 @@ import java.util.Map;
// to a different position, so drop the event. See also [Internal: b/159111848].
return
;
}
checkState
(
adMediaInfo
.
equals
(
imaAdMediaInfo
));
if
(
configuration
.
debugModeEnabled
&&
!
adMediaInfo
.
equals
(
imaAdMediaInfo
))
{
Log
.
w
(
TAG
,
"Unexpected pauseAd for "
+
getAdMediaInfoString
(
adMediaInfo
)
+
", expected "
+
getAdMediaInfoString
(
imaAdMediaInfo
));
}
imaAdState
=
IMA_AD_STATE_PAUSED
;
for
(
int
i
=
0
;
i
<
adCallbacks
.
size
();
i
++)
{
adCallbacks
.
get
(
i
).
onPause
(
adMediaInfo
);
...
...
@@ -1157,9 +1186,13 @@ import java.util.Map;
throw
new
IllegalStateException
(
"Failed to find cue point"
);
}
private
String
getAdMediaInfoString
(
AdMediaInfo
adMediaInfo
)
{
private
String
getAdMediaInfoString
(
@Nullable
AdMediaInfo
adMediaInfo
)
{
@Nullable
AdInfo
adInfo
=
adInfoByAdMediaInfo
.
get
(
adMediaInfo
);
return
"AdMediaInfo["
+
adMediaInfo
.
getUrl
()
+
(
adInfo
!=
null
?
", "
+
adInfo
:
""
)
+
"]"
;
return
"AdMediaInfo["
+
(
adMediaInfo
==
null
?
"null"
:
adMediaInfo
.
getUrl
())
+
", "
+
adInfo
+
"]"
;
}
private
static
long
getContentPeriodPositionMs
(
...
...
@@ -1226,16 +1259,12 @@ import java.util.Map;
if
(
configuration
.
applicationAdEventListener
!=
null
)
{
adsManager
.
addAdEventListener
(
configuration
.
applicationAdEventListener
);
}
if
(
player
!=
null
)
{
// If a player is attached already, start playback immediately.
try
{
adPlaybackState
=
new
AdPlaybackState
(
adsId
,
ImaUtil
.
getAdGroupTimesUsForCuePoints
(
adsManager
.
getAdCuePoints
()));
updateAdPlaybackState
();
}
catch
(
RuntimeException
e
)
{
maybeNotifyInternalError
(
"onAdsManagerLoaded"
,
e
);
}
try
{
adPlaybackState
=
new
AdPlaybackState
(
adsId
,
getAdGroupTimesUsForCuePoints
(
adsManager
.
getAdCuePoints
()));
updateAdPlaybackState
();
}
catch
(
RuntimeException
e
)
{
maybeNotifyInternalError
(
"onAdsManagerLoaded"
,
e
);
}
}
...
...
extensions/ima/src/main/java/com/google/android/exoplayer2/ext/ima/ImaAdsLoader.java
View file @
05f6d248
...
...
@@ -44,6 +44,7 @@ import com.google.ads.interactivemedia.v3.api.player.VideoAdPlayer;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.ExoPlayerLibraryInfo
;
import
com.google.android.exoplayer2.Player
;
import
com.google.android.exoplayer2.Timeline
;
import
com.google.android.exoplayer2.source.MediaSourceFactory
;
import
com.google.android.exoplayer2.source.ads.AdsLoader
;
import
com.google.android.exoplayer2.source.ads.AdsMediaSource
;
...
...
@@ -57,6 +58,7 @@ import java.util.ArrayList;
import
java.util.Arrays
;
import
java.util.Collection
;
import
java.util.Collections
;
import
java.util.HashMap
;
import
java.util.List
;
import
java.util.Locale
;
import
java.util.Set
;
...
...
@@ -371,12 +373,16 @@ public final class ImaAdsLoader implements Player.EventListener, AdsLoader {
private
final
ImaUtil
.
Configuration
configuration
;
private
final
Context
context
;
private
final
ImaUtil
.
ImaFactory
imaFactory
;
private
final
HashMap
<
Object
,
AdTagLoader
>
adTagLoaderByAdsId
;
private
final
HashMap
<
AdsMediaSource
,
AdTagLoader
>
adTagLoaderByAdsMediaSource
;
private
final
Timeline
.
Period
period
;
private
final
Timeline
.
Window
window
;
private
boolean
wasSetPlayerCalled
;
@Nullable
private
Player
nextPlayer
;
@Nullable
private
AdTagLoader
adTagLoader
;
private
List
<
String
>
supportedMimeTypes
;
@Nullable
private
Player
player
;
@Nullable
private
AdTagLoader
currentAdTagLoader
;
private
ImaAdsLoader
(
Context
context
,
ImaUtil
.
Configuration
configuration
,
ImaUtil
.
ImaFactory
imaFactory
)
{
...
...
@@ -384,6 +390,10 @@ public final class ImaAdsLoader implements Player.EventListener, AdsLoader {
this
.
configuration
=
configuration
;
this
.
imaFactory
=
imaFactory
;
supportedMimeTypes
=
ImmutableList
.
of
();
adTagLoaderByAdsId
=
new
HashMap
<>();
adTagLoaderByAdsMediaSource
=
new
HashMap
<>();
period
=
new
Timeline
.
Period
();
window
=
new
Timeline
.
Window
();
}
/**
...
...
@@ -394,7 +404,7 @@ public final class ImaAdsLoader implements Player.EventListener, AdsLoader {
@SuppressWarnings
(
"nullness:nullness.on.outer"
)
@Nullable
public
com
.
google
.
ads
.
interactivemedia
.
v3
.
api
.
AdsLoader
getAdsLoader
()
{
return
adTagLoader
!=
null
?
a
dTagLoader
.
getAdsLoader
()
:
null
;
return
currentAdTagLoader
!=
null
?
currentA
dTagLoader
.
getAdsLoader
()
:
null
;
}
/**
...
...
@@ -410,7 +420,7 @@ public final class ImaAdsLoader implements Player.EventListener, AdsLoader {
*/
@Nullable
public
AdDisplayContainer
getAdDisplayContainer
()
{
return
adTagLoader
!=
null
?
a
dTagLoader
.
getAdDisplayContainer
()
:
null
;
return
currentAdTagLoader
!=
null
?
currentA
dTagLoader
.
getAdDisplayContainer
()
:
null
;
}
/**
...
...
@@ -427,8 +437,8 @@ public final class ImaAdsLoader implements Player.EventListener, AdsLoader {
* null} if playing audio-only ads.
*/
public
void
requestAds
(
DataSpec
adTagDataSpec
,
Object
adsId
,
@Nullable
ViewGroup
adViewGroup
)
{
if
(
adTagLoader
==
null
)
{
adTagLoader
=
if
(
!
adTagLoaderByAdsId
.
containsKey
(
adsId
)
)
{
AdTagLoader
adTagLoader
=
new
AdTagLoader
(
context
,
configuration
,
...
...
@@ -437,6 +447,7 @@ public final class ImaAdsLoader implements Player.EventListener, AdsLoader {
adTagDataSpec
,
adsId
,
adViewGroup
);
adTagLoaderByAdsId
.
put
(
adsId
,
adTagLoader
);
}
}
...
...
@@ -448,8 +459,8 @@ public final class ImaAdsLoader implements Player.EventListener, AdsLoader {
* IMA SDK provides the UI to skip ads in the ad view group passed via {@link AdViewProvider}.
*/
public
void
skipAd
()
{
if
(
a
dTagLoader
!=
null
)
{
a
dTagLoader
.
skipAd
();
if
(
currentA
dTagLoader
!=
null
)
{
currentA
dTagLoader
.
skipAd
();
}
}
...
...
@@ -494,37 +505,67 @@ public final class ImaAdsLoader implements Player.EventListener, AdsLoader {
EventListener
eventListener
)
{
checkState
(
wasSetPlayerCalled
,
"Set player using adsLoader.setPlayer before preparing the player."
);
player
=
nextPlayer
;
@Nullable
Player
player
=
this
.
player
;
if
(
player
==
null
)
{
return
;
if
(
adTagLoaderByAdsMediaSource
.
isEmpty
())
{
player
=
nextPlayer
;
@Nullable
Player
player
=
this
.
player
;
if
(
player
==
null
)
{
return
;
}
player
.
addListener
(
this
);
}
@Nullable
AdTagLoader
adTagLoader
=
adTagLoaderByAdsId
.
get
(
adsId
);
if
(
adTagLoader
==
null
)
{
requestAds
(
adTagDataSpec
,
adsId
,
adViewProvider
.
getAdViewGroup
());
adTagLoader
=
adTagLoaderByAdsId
.
get
(
adsId
);
}
checkNotNull
(
adTagLoader
).
start
(
player
,
adViewProvider
,
eventListener
);
adTagLoaderByAdsMediaSource
.
put
(
adsMediaSource
,
checkNotNull
(
adTagLoader
));
checkNotNull
(
adTagLoader
).
start
(
adViewProvider
,
eventListener
);
maybeUpdateCurrentAdTagLoader
();
}
@Override
public
void
stop
(
AdsMediaSource
adsMediaSource
)
{
if
(
player
!=
null
&&
adTagLoader
!=
null
)
{
adTagLoader
.
stop
();
@Nullable
AdTagLoader
removedAdTagLoader
=
adTagLoaderByAdsMediaSource
.
remove
(
adsMediaSource
);
maybeUpdateCurrentAdTagLoader
();
if
(
removedAdTagLoader
!=
null
)
{
removedAdTagLoader
.
stop
();
}
if
(
player
!=
null
&&
adTagLoaderByAdsMediaSource
.
isEmpty
())
{
player
.
removeListener
(
this
);
player
=
null
;
}
}
@Override
public
void
release
()
{
if
(
adTagLoader
!=
null
)
{
if
(
player
!=
null
)
{
player
.
removeListener
(
this
);
player
=
null
;
maybeUpdateCurrentAdTagLoader
();
}
nextPlayer
=
null
;
for
(
AdTagLoader
adTagLoader
:
adTagLoaderByAdsMediaSource
.
values
())
{
adTagLoader
.
release
();
}
adTagLoaderByAdsMediaSource
.
clear
();
for
(
AdTagLoader
adTagLoader
:
adTagLoaderByAdsId
.
values
())
{
adTagLoader
.
release
();
}
adTagLoaderByAdsId
.
clear
();
}
@Override
public
void
handlePrepareComplete
(
AdsMediaSource
adsMediaSource
,
int
adGroupIndex
,
int
adIndexInAdGroup
)
{
if
(
adTagLoader
!
=
null
)
{
adTagLoader
.
handlePrepareComplete
(
adGroupIndex
,
adIndexInAdGroup
)
;
if
(
player
=
=
null
)
{
return
;
}
checkNotNull
(
adTagLoaderByAdsMediaSource
.
get
(
adsMediaSource
))
.
handlePrepareComplete
(
adGroupIndex
,
adIndexInAdGroup
);
}
@Override
...
...
@@ -533,9 +574,112 @@ public final class ImaAdsLoader implements Player.EventListener, AdsLoader {
int
adGroupIndex
,
int
adIndexInAdGroup
,
IOException
exception
)
{
if
(
adTagLoader
!=
null
)
{
adTagLoader
.
handlePrepareError
(
adGroupIndex
,
adIndexInAdGroup
,
exception
);
if
(
player
==
null
)
{
return
;
}
checkNotNull
(
adTagLoaderByAdsMediaSource
.
get
(
adsMediaSource
))
.
handlePrepareError
(
adGroupIndex
,
adIndexInAdGroup
,
exception
);
}
// Player.EventListener implementation.
@Override
public
void
onTimelineChanged
(
Timeline
timeline
,
@Player
.
TimelineChangeReason
int
reason
)
{
if
(
timeline
.
isEmpty
())
{
// The player is being reset or contains no media.
return
;
}
maybeUpdateCurrentAdTagLoader
();
maybePreloadNextPeriodAds
();
}
@Override
public
void
onPositionDiscontinuity
(
@Player
.
DiscontinuityReason
int
reason
)
{
maybeUpdateCurrentAdTagLoader
();
maybePreloadNextPeriodAds
();
}
@Override
public
void
onShuffleModeEnabledChanged
(
boolean
shuffleModeEnabled
)
{
maybePreloadNextPeriodAds
();
}
@Override
public
void
onRepeatModeChanged
(
@Player
.
RepeatMode
int
repeatMode
)
{
maybePreloadNextPeriodAds
();
}
// Internal methods.
private
void
maybeUpdateCurrentAdTagLoader
()
{
@Nullable
AdTagLoader
oldAdTagLoader
=
currentAdTagLoader
;
@Nullable
AdTagLoader
newAdTagLoader
=
getCurrentAdTagLoader
();
if
(!
Util
.
areEqual
(
oldAdTagLoader
,
newAdTagLoader
))
{
if
(
oldAdTagLoader
!=
null
)
{
oldAdTagLoader
.
deactivate
();
}
currentAdTagLoader
=
newAdTagLoader
;
if
(
newAdTagLoader
!=
null
)
{
newAdTagLoader
.
activate
(
checkNotNull
(
player
));
}
}
}
@Nullable
private
AdTagLoader
getCurrentAdTagLoader
()
{
@Nullable
Player
player
=
this
.
player
;
if
(
player
==
null
)
{
return
null
;
}
Timeline
timeline
=
player
.
getCurrentTimeline
();
if
(
timeline
.
isEmpty
())
{
return
null
;
}
int
periodIndex
=
player
.
getCurrentPeriodIndex
();
@Nullable
Object
adsId
=
timeline
.
getPeriod
(
periodIndex
,
period
).
getAdsId
();
if
(
adsId
==
null
)
{
return
null
;
}
@Nullable
AdTagLoader
adTagLoader
=
adTagLoaderByAdsId
.
get
(
adsId
);
if
(
adTagLoader
==
null
||
!
adTagLoaderByAdsMediaSource
.
containsValue
(
adTagLoader
))
{
return
null
;
}
return
adTagLoader
;
}
private
void
maybePreloadNextPeriodAds
()
{
@Nullable
Player
player
=
this
.
player
;
if
(
player
==
null
)
{
return
;
}
Timeline
timeline
=
player
.
getCurrentTimeline
();
if
(
timeline
.
isEmpty
())
{
return
;
}
int
nextPeriodIndex
=
timeline
.
getNextPeriodIndex
(
player
.
getCurrentPeriodIndex
(),
period
,
window
,
player
.
getRepeatMode
(),
player
.
getShuffleModeEnabled
());
if
(
nextPeriodIndex
==
C
.
INDEX_UNSET
)
{
return
;
}
timeline
.
getPeriod
(
nextPeriodIndex
,
period
);
@Nullable
Object
nextAdsId
=
period
.
getAdsId
();
if
(
nextAdsId
==
null
)
{
return
;
}
@Nullable
AdTagLoader
nextAdTagLoader
=
adTagLoaderByAdsId
.
get
(
nextAdsId
);
if
(
nextAdTagLoader
==
null
||
nextAdTagLoader
==
currentAdTagLoader
)
{
return
;
}
long
periodPositionUs
=
timeline
.
getPeriodPosition
(
window
,
period
,
period
.
windowIndex
,
/* windowPositionUs= */
C
.
TIME_UNSET
)
.
second
;
nextAdTagLoader
.
maybePreloadAds
(
C
.
usToMs
(
periodPositionUs
),
C
.
usToMs
(
period
.
durationUs
));
}
/**
...
...
extensions/ima/src/test/java/com/google/android/exoplayer2/ext/ima/FakePlayer.java
View file @
05f6d248
...
...
@@ -21,25 +21,30 @@ import com.google.android.exoplayer2.Player;
import
com.google.android.exoplayer2.Timeline
;
import
com.google.android.exoplayer2.testutil.StubExoPlayer
;
import
com.google.android.exoplayer2.trackselection.TrackSelectionArray
;
import
java.util.ArrayLis
t
;
import
com.google.android.exoplayer2.util.ListenerSe
t
;
/** A fake player for testing content/ad playback. */
/* package */
final
class
FakePlayer
extends
StubExoPlayer
{
private
final
ArrayList
<
Player
.
EventListener
>
listeners
;
private
final
ListenerSet
<
EventListener
,
Events
>
listeners
;
private
final
Timeline
.
Period
period
;
private
final
Timeline
timeline
;
private
Timeline
timeline
;
@Player
.
State
private
int
state
;
private
boolean
playWhenReady
;
private
long
position
;
private
long
contentPosition
;
private
int
periodIndex
;
private
long
positionMs
;
private
long
contentPositionMs
;
private
boolean
isPlayingAd
;
private
int
adGroupIndex
;
private
int
adIndexInAdGroup
;
public
FakePlayer
()
{
listeners
=
new
ArrayList
<>();
listeners
=
new
ListenerSet
<>(
Looper
.
getMainLooper
(),
Player
.
Events
::
new
,
(
listener
,
eventFlags
)
->
listener
.
onEvents
(
/* player= */
this
,
eventFlags
));
period
=
new
Timeline
.
Period
();
state
=
Player
.
STATE_IDLE
;
playWhenReady
=
true
;
...
...
@@ -48,26 +53,27 @@ import java.util.ArrayList;
/** Sets the timeline on this fake player, which notifies listeners with the changed timeline. */
public
void
updateTimeline
(
Timeline
timeline
,
@TimelineChangeReason
int
reason
)
{
for
(
Player
.
EventListener
listener
:
listeners
)
{
listener
.
onTimelineChanged
(
timeline
,
reason
);
}
this
.
timeline
=
timeline
;
listeners
.
sendEvent
(
Player
.
EVENT_TIMELINE_CHANGED
,
listener
->
listener
.
onTimelineChanged
(
timeline
,
reason
));
}
/**
* Sets the state of this player as if it were playing content at the given {@code position}. If
* an ad is currently playing, this will trigger a position discontinuity.
*/
public
void
setPlayingContentPosition
(
long
position
)
{
public
void
setPlayingContentPosition
(
int
periodIndex
,
long
positionMs
)
{
boolean
notify
=
isPlayingAd
;
isPlayingAd
=
false
;
adGroupIndex
=
C
.
INDEX_UNSET
;
adIndexInAdGroup
=
C
.
INDEX_UNSET
;
this
.
position
=
position
;
contentPosition
=
position
;
this
.
periodIndex
=
periodIndex
;
this
.
positionMs
=
positionMs
;
contentPositionMs
=
positionMs
;
if
(
notify
)
{
for
(
Player
.
EventListener
listener
:
listeners
)
{
listener
.
onPositionDiscontinuity
(
DISCONTINUITY_REASON_AD_INSERTION
);
}
listeners
.
sendEvent
(
Player
.
EVENT_POSITION_DISCONTINUITY
,
listener
->
listener
.
onPositionDiscontinuity
(
DISCONTINUITY_REASON_AD_INSERTION
));
}
}
...
...
@@ -77,17 +83,22 @@ import java.util.ArrayList;
* position discontinuity.
*/
public
void
setPlayingAdPosition
(
int
adGroupIndex
,
int
adIndexInAdGroup
,
long
position
,
long
contentPosition
)
{
int
periodIndex
,
int
adGroupIndex
,
int
adIndexInAdGroup
,
long
positionMs
,
long
contentPositionMs
)
{
boolean
notify
=
!
isPlayingAd
||
this
.
adIndexInAdGroup
!=
adIndexInAdGroup
;
isPlayingAd
=
true
;
this
.
periodIndex
=
periodIndex
;
this
.
adGroupIndex
=
adGroupIndex
;
this
.
adIndexInAdGroup
=
adIndexInAdGroup
;
this
.
position
=
position
;
this
.
contentPosition
=
contentPosition
;
this
.
position
Ms
=
positionMs
;
this
.
contentPosition
Ms
=
contentPositionMs
;
if
(
notify
)
{
for
(
Player
.
EventListener
listener
:
listeners
)
{
listener
.
onPositionDiscontinuity
(
DISCONTINUITY_REASON_AD_INSERTION
);
}
listeners
.
sendEvent
(
EVENT_POSITION_DISCONTINUITY
,
listener
->
listener
.
onPositionDiscontinuity
(
DISCONTINUITY_REASON_AD_INSERTION
));
}
}
...
...
@@ -99,16 +110,18 @@ import java.util.ArrayList;
this
.
state
=
state
;
this
.
playWhenReady
=
playWhenReady
;
if
(
playbackStateChanged
||
playWhenReadyChanged
)
{
for
(
Player
.
EventListener
listener
:
listeners
)
{
listener
.
onPlayerStateChanged
(
playWhenReady
,
state
);
if
(
playbackStateChanged
)
{
listener
.
onPlaybackStateChanged
(
state
);
}
if
(
playWhenReadyChanged
)
{
listener
.
onPlayWhenReadyChanged
(
playWhenReady
,
PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST
);
}
}
listeners
.
sendEvent
(
Player
.
EVENT_PLAYBACK_STATE_CHANGED
,
listener
->
{
listener
.
onPlayerStateChanged
(
playWhenReady
,
state
);
if
(
playbackStateChanged
)
{
listener
.
onPlaybackStateChanged
(
state
);
}
if
(
playWhenReadyChanged
)
{
listener
.
onPlayWhenReadyChanged
(
playWhenReady
,
PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST
);
}
});
}
}
...
...
@@ -146,6 +159,17 @@ import java.util.ArrayList;
}
@Override
@RepeatMode
public
int
getRepeatMode
()
{
return
REPEAT_MODE_OFF
;
}
@Override
public
boolean
getShuffleModeEnabled
()
{
return
false
;
}
@Override
public
int
getRendererCount
()
{
return
0
;
}
...
...
@@ -162,7 +186,7 @@ import java.util.ArrayList;
@Override
public
int
getCurrentPeriodIndex
()
{
return
0
;
return
periodIndex
;
}
@Override
...
...
@@ -186,7 +210,7 @@ import java.util.ArrayList;
@Override
public
long
getCurrentPosition
()
{
return
position
;
return
position
Ms
;
}
@Override
...
...
@@ -206,6 +230,6 @@ import java.util.ArrayList;
@Override
public
long
getContentPosition
()
{
return
contentPosition
;
return
contentPosition
Ms
;
}
}
extensions/ima/src/test/java/com/google/android/exoplayer2/ext/ima/ImaAdsLoaderTest.java
View file @
05f6d248
...
...
@@ -53,18 +53,15 @@ import com.google.ads.interactivemedia.v3.api.player.VideoAdPlayer;
import
com.google.ads.interactivemedia.v3.api.player.VideoProgressUpdate
;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.ExoPlaybackException
;
import
com.google.android.exoplayer2.MediaItem
;
import
com.google.android.exoplayer2.Player
;
import
com.google.android.exoplayer2.Timeline
;
import
com.google.android.exoplayer2.Timeline.Period
;
import
com.google.android.exoplayer2.ext.ima.ImaUtil.ImaFactory
;
import
com.google.android.exoplayer2.source.DefaultMediaSourceFactory
;
import
com.google.android.exoplayer2.source.MaskingMediaSource.PlaceholderTimeline
;
import
com.google.android.exoplayer2.source.ads.AdPlaybackState
;
import
com.google.android.exoplayer2.source.ads.AdsLoader
;
import
com.google.android.exoplayer2.source.ads.AdsMediaSource
;
import
com.google.android.exoplayer2.source.ads.AdsMediaSource.AdLoadException
;
import
com.google.android.exoplayer2.source.ads.SinglePeriodAdTimeline
;
import
com.google.android.exoplayer2.testutil.FakeMediaSource
;
import
com.google.android.exoplayer2.testutil.FakeTimeline
;
import
com.google.android.exoplayer2.testutil.FakeTimeline.TimelineWindowDefinition
;
...
...
@@ -133,12 +130,13 @@ public final class ImaAdsLoaderTest {
private
ContentProgressProvider
contentProgressProvider
;
private
VideoAdPlayer
videoAdPlayer
;
private
TestAdsLoaderListener
adsLoaderListener
;
private
FakePlayer
fake
Exo
Player
;
private
FakePlayer
fakePlayer
;
private
ImaAdsLoader
imaAdsLoader
;
@Before
public
void
setUp
()
{
setupMocks
();
fakePlayer
=
new
FakePlayer
();
adViewGroup
=
new
FrameLayout
(
getApplicationContext
());
View
adOverlayView
=
new
View
(
getApplicationContext
());
adViewProvider
=
...
...
@@ -166,19 +164,32 @@ public final class ImaAdsLoaderTest {
return
ImmutableList
.
of
();
}
};
imaAdsLoader
=
new
ImaAdsLoader
.
Builder
(
getApplicationContext
())
.
setImaFactory
(
mockImaFactory
)
.
setImaSdkSettings
(
mockImaSdkSettings
)
.
build
();
imaAdsLoader
.
setPlayer
(
fakePlayer
);
adsMediaSource
=
new
AdsMediaSource
(
new
FakeMediaSource
(
CONTENT_TIMELINE
),
TEST_DATA_SPEC
,
TEST_ADS_ID
,
new
DefaultMediaSourceFactory
((
Context
)
getApplicationContext
()),
imaAdsLoader
,
adViewProvider
);
adsLoaderListener
=
new
TestAdsLoaderListener
(
getInitialTimelineWindowDefinition
(
TEST_ADS_ID
));
when
(
mockAdsManager
.
getAdCuePoints
()).
thenReturn
(
PREROLL_CUE_POINTS_SECONDS
);
}
@After
public
void
teardown
()
{
if
(
imaAdsLoader
!=
null
)
{
imaAdsLoader
.
release
();
}
imaAdsLoader
.
release
();
}
@Test
public
void
builder_overrides
PlayerType
()
{
public
void
loader_overridesCustom
PlayerType
()
{
when
(
mockImaSdkSettings
.
getPlayerType
()).
thenReturn
(
"test player type"
);
setupPlayback
(
CONTENT_TIMELINE
,
PREROLL_CUE_POINTS_SECONDS
);
imaAdsLoader
.
start
(
adsMediaSource
,
TEST_DATA_SPEC
,
TEST_ADS_ID
,
adViewProvider
,
adsLoaderListener
);
...
...
@@ -187,7 +198,6 @@ public final class ImaAdsLoaderTest {
@Test
public
void
start_setsAdUiViewGroup
()
{
setupPlayback
(
CONTENT_TIMELINE
,
PREROLL_CUE_POINTS_SECONDS
);
imaAdsLoader
.
start
(
adsMediaSource
,
TEST_DATA_SPEC
,
TEST_ADS_ID
,
adViewProvider
,
adsLoaderListener
);
...
...
@@ -198,7 +208,6 @@ public final class ImaAdsLoaderTest {
@Test
public
void
startForAudioOnlyAds_createsAudioOnlyAdDisplayContainer
()
{
setupPlayback
(
CONTENT_TIMELINE
,
PREROLL_CUE_POINTS_SECONDS
);
imaAdsLoader
.
start
(
adsMediaSource
,
TEST_DATA_SPEC
,
TEST_ADS_ID
,
audioAdsAdViewProvider
,
adsLoaderListener
);
...
...
@@ -210,8 +219,11 @@ public final class ImaAdsLoaderTest {
@Test
public
void
start_withPlaceholderContent_initializedAdsLoader
()
{
Timeline
placeholderTimeline
=
new
PlaceholderTimeline
(
MediaItem
.
fromUri
(
Uri
.
EMPTY
));
setupPlayback
(
placeholderTimeline
,
PREROLL_CUE_POINTS_SECONDS
);
adsLoaderListener
=
new
TestAdsLoaderListener
(
getInitialTimelineWindowDefinition
(
TEST_ADS_ID
,
/* isPlaceholder= */
true
));
when
(
mockAdsManager
.
getAdCuePoints
()).
thenReturn
(
PREROLL_CUE_POINTS_SECONDS
);
imaAdsLoader
.
start
(
adsMediaSource
,
TEST_DATA_SPEC
,
TEST_ADS_ID
,
adViewProvider
,
adsLoaderListener
);
...
...
@@ -221,11 +233,10 @@ public final class ImaAdsLoaderTest {
@Test
public
void
start_updatesAdPlaybackState
()
{
setupPlayback
(
CONTENT_TIMELINE
,
PREROLL_CUE_POINTS_SECONDS
);
imaAdsLoader
.
start
(
adsMediaSource
,
TEST_DATA_SPEC
,
TEST_ADS_ID
,
adViewProvider
,
adsLoaderListener
);
assertThat
(
adsLoaderListener
.
adPlaybackState
)
assertThat
(
adsLoaderListener
.
getAdPlaybackState
(
/* periodIndex= */
0
)
)
.
isEqualTo
(
new
AdPlaybackState
(
TEST_ADS_ID
,
/* adGroupTimesUs...= */
0
)
.
withContentDurationUs
(
CONTENT_PERIOD_DURATION_US
));
...
...
@@ -233,7 +244,6 @@ public final class ImaAdsLoaderTest {
@Test
public
void
startAfterRelease
()
{
setupPlayback
(
CONTENT_TIMELINE
,
PREROLL_CUE_POINTS_SECONDS
);
imaAdsLoader
.
release
();
imaAdsLoader
.
start
(
adsMediaSource
,
TEST_DATA_SPEC
,
TEST_ADS_ID
,
adViewProvider
,
adsLoaderListener
);
...
...
@@ -241,14 +251,13 @@ public final class ImaAdsLoaderTest {
@Test
public
void
startAndCallbacksAfterRelease
()
{
setupPlayback
(
CONTENT_TIMELINE
,
PREROLL_CUE_POINTS_SECONDS
);
// Request ads in order to get a reference to the ad event listener.
imaAdsLoader
.
requestAds
(
TEST_DATA_SPEC
,
TEST_ADS_ID
,
adViewGroup
);
imaAdsLoader
.
release
();
imaAdsLoader
.
start
(
adsMediaSource
,
TEST_DATA_SPEC
,
TEST_ADS_ID
,
adViewProvider
,
adsLoaderListener
);
fake
ExoPlayer
.
setPlayingContentPosition
(
/* position
= */
0
);
fake
Exo
Player
.
setState
(
Player
.
STATE_READY
,
true
);
fake
Player
.
setPlayingContentPosition
(
/* periodIndex= */
0
,
/* positionMs
= */
0
);
fakePlayer
.
setState
(
Player
.
STATE_READY
,
true
);
// If callbacks are invoked there is no crash.
// Note: we can't currently call getContentProgress/getAdProgress as a VerifyError is thrown
...
...
@@ -271,8 +280,6 @@ public final class ImaAdsLoaderTest {
@Test
public
void
playback_withPrerollAd_marksAdAsPlayed
()
{
setupPlayback
(
CONTENT_TIMELINE
,
PREROLL_CUE_POINTS_SECONDS
);
// Load the preroll ad.
imaAdsLoader
.
start
(
adsMediaSource
,
TEST_DATA_SPEC
,
TEST_ADS_ID
,
adViewProvider
,
adsLoaderListener
);
...
...
@@ -282,24 +289,25 @@ public final class ImaAdsLoaderTest {
// Play the preroll ad.
videoAdPlayer
.
playAd
(
TEST_AD_MEDIA_INFO
);
fakeExoPlayer
.
setPlayingAdPosition
(
fakePlayer
.
setPlayingAdPosition
(
/* periodIndex= */
0
,
/* adGroupIndex= */
0
,
/* adIndexInAdGroup= */
0
,
/* position= */
0
,
/* contentPosition= */
0
);
fake
Exo
Player
.
setState
(
Player
.
STATE_READY
,
true
);
fakePlayer
.
setState
(
Player
.
STATE_READY
,
true
);
adEventListener
.
onAdEvent
(
getAdEvent
(
AdEventType
.
STARTED
,
mockPrerollSingleAd
));
adEventListener
.
onAdEvent
(
getAdEvent
(
AdEventType
.
FIRST_QUARTILE
,
mockPrerollSingleAd
));
adEventListener
.
onAdEvent
(
getAdEvent
(
AdEventType
.
MIDPOINT
,
mockPrerollSingleAd
));
adEventListener
.
onAdEvent
(
getAdEvent
(
AdEventType
.
THIRD_QUARTILE
,
mockPrerollSingleAd
));
// Play the content.
fake
ExoPlayer
.
setPlayingContentPosition
(
0
);
fake
Player
.
setPlayingContentPosition
(
/* periodIndex= */
0
,
/* positionMs= */
0
);
videoAdPlayer
.
stopAd
(
TEST_AD_MEDIA_INFO
);
adEventListener
.
onAdEvent
(
getAdEvent
(
AdEventType
.
CONTENT_RESUME_REQUESTED
,
/* ad= */
null
));
// Verify that the preroll ad has been marked as played.
assertThat
(
adsLoaderListener
.
adPlaybackState
)
assertThat
(
adsLoaderListener
.
getAdPlaybackState
(
/* periodIndex= */
0
)
)
.
isEqualTo
(
new
AdPlaybackState
(
TEST_ADS_ID
,
/* adGroupTimesUs...= */
0
)
.
withContentDurationUs
(
CONTENT_PERIOD_DURATION_US
)
...
...
@@ -316,14 +324,14 @@ public final class ImaAdsLoaderTest {
when
(
mockMidrollFetchErrorAdEvent
.
getType
()).
thenReturn
(
AdEventType
.
AD_BREAK_FETCH_ERROR
);
when
(
mockMidrollFetchErrorAdEvent
.
getAdData
())
.
thenReturn
(
ImmutableMap
.
of
(
"adBreakTime"
,
"20.5"
));
setupPlayback
(
CONTENT_TIMELINE
,
ImmutableList
.
of
(
20.5f
));
when
(
mockAdsManager
.
getAdCuePoints
()).
thenReturn
(
ImmutableList
.
of
(
20.5f
));
// Simulate loading an empty midroll ad.
imaAdsLoader
.
start
(
adsMediaSource
,
TEST_DATA_SPEC
,
TEST_ADS_ID
,
adViewProvider
,
adsLoaderListener
);
adEventListener
.
onAdEvent
(
mockMidrollFetchErrorAdEvent
);
assertThat
(
adsLoaderListener
.
adPlaybackState
)
assertThat
(
adsLoaderListener
.
getAdPlaybackState
(
/* periodIndex= */
0
)
)
.
isEqualTo
(
new
AdPlaybackState
(
TEST_ADS_ID
,
/* adGroupTimesUs...= */
20_500_000
)
.
withContentDurationUs
(
CONTENT_PERIOD_DURATION_US
)
...
...
@@ -338,7 +346,7 @@ public final class ImaAdsLoaderTest {
when
(
mockMidrollFetchErrorAdEvent
.
getType
()).
thenReturn
(
AdEventType
.
AD_BREAK_FETCH_ERROR
);
when
(
mockMidrollFetchErrorAdEvent
.
getAdData
())
.
thenReturn
(
ImmutableMap
.
of
(
"adBreakTime"
,
"5.5"
));
setupPlayback
(
CONTENT_TIMELINE
,
ImmutableList
.
of
(
5.5f
));
when
(
mockAdsManager
.
getAdCuePoints
()).
thenReturn
(
ImmutableList
.
of
(
5.5f
));
// Simulate loading an empty midroll ad and advancing the player position.
imaAdsLoader
.
start
(
...
...
@@ -349,7 +357,7 @@ public final class ImaAdsLoaderTest {
playerPositionUs
+
TimelineWindowDefinition
.
DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US
;
long
periodDurationUs
=
CONTENT_TIMELINE
.
getPeriod
(
/* periodIndex= */
0
,
new
Period
()).
durationUs
;
fake
ExoPlayer
.
setPlayingContentPosition
(
C
.
usToMs
(
playerPositionUs
));
fake
Player
.
setPlayingContentPosition
(
/* periodIndex= */
0
,
C
.
usToMs
(
playerPositionUs
));
// Verify the content progress is updated to reflect the new player position.
assertThat
(
contentProgressProvider
.
getContentProgress
())
...
...
@@ -364,14 +372,14 @@ public final class ImaAdsLoaderTest {
when
(
mockPostrollFetchErrorAdEvent
.
getType
()).
thenReturn
(
AdEventType
.
AD_BREAK_FETCH_ERROR
);
when
(
mockPostrollFetchErrorAdEvent
.
getAdData
())
.
thenReturn
(
ImmutableMap
.
of
(
"adBreakTime"
,
"-1"
));
setupPlayback
(
CONTENT_TIMELINE
,
ImmutableList
.
of
(-
1
f
));
when
(
mockAdsManager
.
getAdCuePoints
()).
thenReturn
(
ImmutableList
.
of
(-
1
f
));
// Simulate loading an empty postroll ad.
imaAdsLoader
.
start
(
adsMediaSource
,
TEST_DATA_SPEC
,
TEST_ADS_ID
,
adViewProvider
,
adsLoaderListener
);
adEventListener
.
onAdEvent
(
mockPostrollFetchErrorAdEvent
);
assertThat
(
adsLoaderListener
.
adPlaybackState
)
assertThat
(
adsLoaderListener
.
getAdPlaybackState
(
/* periodIndex= */
0
)
)
.
isEqualTo
(
new
AdPlaybackState
(
TEST_ADS_ID
,
/* adGroupTimesUs...= */
C
.
TIME_END_OF_SOURCE
)
.
withContentDurationUs
(
CONTENT_PERIOD_DURATION_US
)
...
...
@@ -388,18 +396,18 @@ public final class ImaAdsLoaderTest {
adGroupPositionInWindowUs
+
TimelineWindowDefinition
.
DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US
;
ImmutableList
<
Float
>
cuePoints
=
ImmutableList
.
of
((
float
)
adGroupTimeUs
/
C
.
MICROS_PER_SECOND
);
setupPlayback
(
CONTENT_TIMELINE
,
cuePoints
);
when
(
mockAdsManager
.
getAdCuePoints
()).
thenReturn
(
cuePoints
);
// Advance playback to just before the midroll and simulate buffering.
imaAdsLoader
.
start
(
adsMediaSource
,
TEST_DATA_SPEC
,
TEST_ADS_ID
,
adViewProvider
,
adsLoaderListener
);
fake
ExoPlayer
.
setPlayingContentPosition
(
C
.
usToMs
(
adGroupPositionInWindowUs
));
fake
Exo
Player
.
setState
(
Player
.
STATE_BUFFERING
,
/* playWhenReady= */
true
);
fake
Player
.
setPlayingContentPosition
(
/* periodIndex= */
0
,
C
.
usToMs
(
adGroupPositionInWindowUs
));
fakePlayer
.
setState
(
Player
.
STATE_BUFFERING
,
/* playWhenReady= */
true
);
// Advance before the timeout and simulating polling content progress.
ShadowSystemClock
.
advanceBy
(
Duration
.
ofSeconds
(
1
));
contentProgressProvider
.
getContentProgress
();
assertThat
(
adsLoaderListener
.
adPlaybackState
)
assertThat
(
adsLoaderListener
.
getAdPlaybackState
(
/* periodIndex= */
0
)
)
.
isEqualTo
(
new
AdPlaybackState
(
TEST_ADS_ID
,
getAdGroupTimesUsForCuePoints
(
cuePoints
))
.
withContentDurationUs
(
CONTENT_PERIOD_DURATION_US
));
...
...
@@ -413,18 +421,18 @@ public final class ImaAdsLoaderTest {
adGroupPositionInWindowUs
+
TimelineWindowDefinition
.
DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US
;
ImmutableList
<
Float
>
cuePoints
=
ImmutableList
.
of
((
float
)
adGroupTimeUs
/
C
.
MICROS_PER_SECOND
);
setupPlayback
(
CONTENT_TIMELINE
,
cuePoints
);
when
(
mockAdsManager
.
getAdCuePoints
()).
thenReturn
(
cuePoints
);
// Advance playback to just before the midroll and simulate buffering.
imaAdsLoader
.
start
(
adsMediaSource
,
TEST_DATA_SPEC
,
TEST_ADS_ID
,
adViewProvider
,
adsLoaderListener
);
fake
ExoPlayer
.
setPlayingContentPosition
(
C
.
usToMs
(
adGroupPositionInWindowUs
));
fake
Exo
Player
.
setState
(
Player
.
STATE_BUFFERING
,
/* playWhenReady= */
true
);
fake
Player
.
setPlayingContentPosition
(
/* periodIndex= */
0
,
C
.
usToMs
(
adGroupPositionInWindowUs
));
fakePlayer
.
setState
(
Player
.
STATE_BUFFERING
,
/* playWhenReady= */
true
);
// Advance past the timeout and simulate polling content progress.
ShadowSystemClock
.
advanceBy
(
Duration
.
ofSeconds
(
5
));
contentProgressProvider
.
getContentProgress
();
assertThat
(
adsLoaderListener
.
adPlaybackState
)
assertThat
(
adsLoaderListener
.
getAdPlaybackState
(
/* periodIndex= */
0
)
)
.
isEqualTo
(
new
AdPlaybackState
(
TEST_ADS_ID
,
getAdGroupTimesUsForCuePoints
(
cuePoints
))
.
withContentDurationUs
(
CONTENT_PERIOD_DURATION_US
)
...
...
@@ -440,14 +448,15 @@ public final class ImaAdsLoaderTest {
midrollWindowTimeUs
+
TimelineWindowDefinition
.
DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US
;
ImmutableList
<
Float
>
cuePoints
=
ImmutableList
.
of
(
0
f
,
(
float
)
midrollPeriodTimeUs
/
C
.
MICROS_PER_SECOND
);
setupPlayback
(
CONTENT_TIMELINE
,
cuePoints
);
when
(
mockAdsManager
.
getAdCuePoints
()).
thenReturn
(
cuePoints
);
fakeExoPlayer
.
setPlayingContentPosition
(
C
.
usToMs
(
midrollWindowTimeUs
)
-
1_000
);
fakePlayer
.
setPlayingContentPosition
(
/* periodIndex= */
0
,
C
.
usToMs
(
midrollWindowTimeUs
)
-
1_000
);
imaAdsLoader
.
start
(
adsMediaSource
,
TEST_DATA_SPEC
,
TEST_ADS_ID
,
adViewProvider
,
adsLoaderListener
);
verify
(
mockAdsRenderingSettings
,
never
()).
setPlayAdsAfterTime
(
anyDouble
());
assertThat
(
adsLoaderListener
.
adPlaybackState
)
assertThat
(
adsLoaderListener
.
getAdPlaybackState
(
/* periodIndex= */
0
)
)
.
isEqualTo
(
new
AdPlaybackState
(
TEST_ADS_ID
,
getAdGroupTimesUsForCuePoints
(
cuePoints
))
.
withContentDurationUs
(
CONTENT_PERIOD_DURATION_US
));
...
...
@@ -460,9 +469,9 @@ public final class ImaAdsLoaderTest {
midrollWindowTimeUs
+
TimelineWindowDefinition
.
DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US
;
ImmutableList
<
Float
>
cuePoints
=
ImmutableList
.
of
(
0
f
,
(
float
)
midrollPeriodTimeUs
/
C
.
MICROS_PER_SECOND
);
setupPlayback
(
CONTENT_TIMELINE
,
cuePoints
);
when
(
mockAdsManager
.
getAdCuePoints
()).
thenReturn
(
cuePoints
);
fake
ExoPlayer
.
setPlayingContentPosition
(
C
.
usToMs
(
midrollWindowTimeUs
));
fake
Player
.
setPlayingContentPosition
(
/* periodIndex= */
0
,
C
.
usToMs
(
midrollWindowTimeUs
));
imaAdsLoader
.
start
(
adsMediaSource
,
TEST_DATA_SPEC
,
TEST_ADS_ID
,
adViewProvider
,
adsLoaderListener
);
...
...
@@ -472,7 +481,7 @@ public final class ImaAdsLoaderTest {
assertThat
(
playAdsAfterTimeCaptor
.
getValue
())
.
isWithin
(
0.1
)
.
of
(
expectedPlayAdsAfterTimeUs
/
C
.
MICROS_PER_SECOND
);
assertThat
(
adsLoaderListener
.
adPlaybackState
)
assertThat
(
adsLoaderListener
.
getAdPlaybackState
(
/* periodIndex= */
0
)
)
.
isEqualTo
(
new
AdPlaybackState
(
TEST_ADS_ID
,
getAdGroupTimesUsForCuePoints
(
cuePoints
))
.
withContentDurationUs
(
CONTENT_PERIOD_DURATION_US
)
...
...
@@ -486,9 +495,10 @@ public final class ImaAdsLoaderTest {
midrollWindowTimeUs
+
TimelineWindowDefinition
.
DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US
;
ImmutableList
<
Float
>
cuePoints
=
ImmutableList
.
of
(
0
f
,
(
float
)
midrollPeriodTimeUs
/
C
.
MICROS_PER_SECOND
);
setupPlayback
(
CONTENT_TIMELINE
,
cuePoints
);
when
(
mockAdsManager
.
getAdCuePoints
()).
thenReturn
(
cuePoints
);
fakeExoPlayer
.
setPlayingContentPosition
(
C
.
usToMs
(
midrollWindowTimeUs
)
+
1_000
);
fakePlayer
.
setPlayingContentPosition
(
/* periodIndex= */
0
,
C
.
usToMs
(
midrollWindowTimeUs
)
+
1_000
);
imaAdsLoader
.
start
(
adsMediaSource
,
TEST_DATA_SPEC
,
TEST_ADS_ID
,
adViewProvider
,
adsLoaderListener
);
...
...
@@ -498,7 +508,7 @@ public final class ImaAdsLoaderTest {
assertThat
(
playAdsAfterTimeCaptor
.
getValue
())
.
isWithin
(
0.1
)
.
of
(
expectedPlayAdsAfterTimeUs
/
C
.
MICROS_PER_SECOND
);
assertThat
(
adsLoaderListener
.
adPlaybackState
)
assertThat
(
adsLoaderListener
.
getAdPlaybackState
(
/* periodIndex= */
0
)
)
.
isEqualTo
(
new
AdPlaybackState
(
TEST_ADS_ID
,
getAdGroupTimesUsForCuePoints
(
cuePoints
))
.
withContentDurationUs
(
CONTENT_PERIOD_DURATION_US
)
...
...
@@ -519,14 +529,15 @@ public final class ImaAdsLoaderTest {
ImmutableList
.
of
(
(
float
)
firstMidrollPeriodTimeUs
/
C
.
MICROS_PER_SECOND
,
(
float
)
secondMidrollPeriodTimeUs
/
C
.
MICROS_PER_SECOND
);
setupPlayback
(
CONTENT_TIMELINE
,
cuePoints
);
when
(
mockAdsManager
.
getAdCuePoints
()).
thenReturn
(
cuePoints
);
fakeExoPlayer
.
setPlayingContentPosition
(
C
.
usToMs
(
secondMidrollWindowTimeUs
)
-
1_000
);
fakePlayer
.
setPlayingContentPosition
(
/* periodIndex= */
0
,
C
.
usToMs
(
secondMidrollWindowTimeUs
)
-
1_000
);
imaAdsLoader
.
start
(
adsMediaSource
,
TEST_DATA_SPEC
,
TEST_ADS_ID
,
adViewProvider
,
adsLoaderListener
);
verify
(
mockAdsRenderingSettings
,
never
()).
setPlayAdsAfterTime
(
anyDouble
());
assertThat
(
adsLoaderListener
.
adPlaybackState
)
assertThat
(
adsLoaderListener
.
getAdPlaybackState
(
/* periodIndex= */
0
)
)
.
isEqualTo
(
new
AdPlaybackState
(
TEST_ADS_ID
,
getAdGroupTimesUsForCuePoints
(
cuePoints
))
.
withContentDurationUs
(
CONTENT_PERIOD_DURATION_US
));
...
...
@@ -546,9 +557,9 @@ public final class ImaAdsLoaderTest {
ImmutableList
.
of
(
(
float
)
firstMidrollPeriodTimeUs
/
C
.
MICROS_PER_SECOND
,
(
float
)
secondMidrollPeriodTimeUs
/
C
.
MICROS_PER_SECOND
);
setupPlayback
(
CONTENT_TIMELINE
,
cuePoints
);
when
(
mockAdsManager
.
getAdCuePoints
()).
thenReturn
(
cuePoints
);
fake
ExoPlayer
.
setPlayingContentPosition
(
C
.
usToMs
(
secondMidrollWindowTimeUs
));
fake
Player
.
setPlayingContentPosition
(
/* periodIndex= */
0
,
C
.
usToMs
(
secondMidrollWindowTimeUs
));
imaAdsLoader
.
start
(
adsMediaSource
,
TEST_DATA_SPEC
,
TEST_ADS_ID
,
adViewProvider
,
adsLoaderListener
);
...
...
@@ -558,7 +569,7 @@ public final class ImaAdsLoaderTest {
assertThat
(
playAdsAfterTimeCaptor
.
getValue
())
.
isWithin
(
0.1
)
.
of
(
expectedPlayAdsAfterTimeUs
/
C
.
MICROS_PER_SECOND
);
assertThat
(
adsLoaderListener
.
adPlaybackState
)
assertThat
(
adsLoaderListener
.
getAdPlaybackState
(
/* periodIndex= */
0
)
)
.
isEqualTo
(
new
AdPlaybackState
(
TEST_ADS_ID
,
getAdGroupTimesUsForCuePoints
(
cuePoints
))
.
withContentDurationUs
(
CONTENT_PERIOD_DURATION_US
)
...
...
@@ -567,23 +578,30 @@ public final class ImaAdsLoaderTest {
@Test
public
void
resumePlaybackBeforeMidroll_withoutPlayAdBeforeStartPosition_skipsPreroll
()
{
imaAdsLoader
=
new
ImaAdsLoader
.
Builder
(
getApplicationContext
())
.
setPlayAdBeforeStartPosition
(
false
)
.
setImaFactory
(
mockImaFactory
)
.
setImaSdkSettings
(
mockImaSdkSettings
)
.
build
();
imaAdsLoader
.
setPlayer
(
fakePlayer
);
adsMediaSource
=
new
AdsMediaSource
(
new
FakeMediaSource
(
CONTENT_TIMELINE
),
TEST_DATA_SPEC
,
TEST_ADS_ID
,
new
DefaultMediaSourceFactory
((
Context
)
getApplicationContext
()),
imaAdsLoader
,
adViewProvider
);
long
midrollWindowTimeUs
=
2
*
C
.
MICROS_PER_SECOND
;
long
midrollPeriodTimeUs
=
midrollWindowTimeUs
+
TimelineWindowDefinition
.
DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US
;
ImmutableList
<
Float
>
cuePoints
=
ImmutableList
.
of
(
0
f
,
(
float
)
midrollPeriodTimeUs
/
C
.
MICROS_PER_SECOND
);
setupPlayback
(
CONTENT_TIMELINE
,
cuePoints
,
new
ImaAdsLoader
.
Builder
(
getApplicationContext
())
.
setPlayAdBeforeStartPosition
(
false
)
.
setImaFactory
(
mockImaFactory
)
.
setImaSdkSettings
(
mockImaSdkSettings
)
.
build
(),
TEST_DATA_SPEC
,
TEST_ADS_ID
);
when
(
mockAdsManager
.
getAdCuePoints
()).
thenReturn
(
cuePoints
);
fakeExoPlayer
.
setPlayingContentPosition
(
C
.
usToMs
(
midrollWindowTimeUs
)
-
1_000
);
fakePlayer
.
setPlayingContentPosition
(
/* periodIndex= */
0
,
C
.
usToMs
(
midrollWindowTimeUs
)
-
1_000
);
imaAdsLoader
.
start
(
adsMediaSource
,
TEST_DATA_SPEC
,
TEST_ADS_ID
,
adViewProvider
,
adsLoaderListener
);
...
...
@@ -593,7 +611,7 @@ public final class ImaAdsLoaderTest {
assertThat
(
playAdsAfterTimeCaptor
.
getValue
())
.
isWithin
(
0.1d
)
.
of
(
expectedPlayAdsAfterTimeUs
/
C
.
MICROS_PER_SECOND
);
assertThat
(
adsLoaderListener
.
adPlaybackState
)
assertThat
(
adsLoaderListener
.
getAdPlaybackState
(
/* periodIndex= */
0
)
)
.
isEqualTo
(
new
AdPlaybackState
(
TEST_ADS_ID
,
getAdGroupTimesUsForCuePoints
(
cuePoints
))
.
withSkippedAdGroup
(
/* adGroupIndex= */
0
)
...
...
@@ -602,23 +620,29 @@ public final class ImaAdsLoaderTest {
@Test
public
void
resumePlaybackAtMidroll_withoutPlayAdBeforeStartPosition_skipsPreroll
()
{
imaAdsLoader
=
new
ImaAdsLoader
.
Builder
(
getApplicationContext
())
.
setPlayAdBeforeStartPosition
(
false
)
.
setImaFactory
(
mockImaFactory
)
.
setImaSdkSettings
(
mockImaSdkSettings
)
.
build
();
imaAdsLoader
.
setPlayer
(
fakePlayer
);
adsMediaSource
=
new
AdsMediaSource
(
new
FakeMediaSource
(
CONTENT_TIMELINE
),
TEST_DATA_SPEC
,
TEST_ADS_ID
,
new
DefaultMediaSourceFactory
((
Context
)
getApplicationContext
()),
imaAdsLoader
,
adViewProvider
);
long
midrollWindowTimeUs
=
2
*
C
.
MICROS_PER_SECOND
;
long
midrollPeriodTimeUs
=
midrollWindowTimeUs
+
TimelineWindowDefinition
.
DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US
;
ImmutableList
<
Float
>
cuePoints
=
ImmutableList
.
of
(
0
f
,
(
float
)
midrollPeriodTimeUs
/
C
.
MICROS_PER_SECOND
);
setupPlayback
(
CONTENT_TIMELINE
,
cuePoints
,
new
ImaAdsLoader
.
Builder
(
getApplicationContext
())
.
setPlayAdBeforeStartPosition
(
false
)
.
setImaFactory
(
mockImaFactory
)
.
setImaSdkSettings
(
mockImaSdkSettings
)
.
build
(),
TEST_DATA_SPEC
,
TEST_ADS_ID
);
when
(
mockAdsManager
.
getAdCuePoints
()).
thenReturn
(
cuePoints
);
fake
ExoPlayer
.
setPlayingContentPosition
(
C
.
usToMs
(
midrollWindowTimeUs
));
fake
Player
.
setPlayingContentPosition
(
/* periodIndex= */
0
,
C
.
usToMs
(
midrollWindowTimeUs
));
imaAdsLoader
.
start
(
adsMediaSource
,
TEST_DATA_SPEC
,
TEST_ADS_ID
,
adViewProvider
,
adsLoaderListener
);
...
...
@@ -628,7 +652,7 @@ public final class ImaAdsLoaderTest {
assertThat
(
playAdsAfterTimeCaptor
.
getValue
())
.
isWithin
(
0.1d
)
.
of
(
expectedPlayAdsAfterTimeUs
/
C
.
MICROS_PER_SECOND
);
assertThat
(
adsLoaderListener
.
adPlaybackState
)
assertThat
(
adsLoaderListener
.
getAdPlaybackState
(
/* periodIndex= */
0
)
)
.
isEqualTo
(
new
AdPlaybackState
(
TEST_ADS_ID
,
getAdGroupTimesUsForCuePoints
(
cuePoints
))
.
withContentDurationUs
(
CONTENT_PERIOD_DURATION_US
)
...
...
@@ -637,28 +661,35 @@ public final class ImaAdsLoaderTest {
@Test
public
void
resumePlaybackAfterMidroll_withoutPlayAdBeforeStartPosition_skipsMidroll
()
{
imaAdsLoader
=
new
ImaAdsLoader
.
Builder
(
getApplicationContext
())
.
setPlayAdBeforeStartPosition
(
false
)
.
setImaFactory
(
mockImaFactory
)
.
setImaSdkSettings
(
mockImaSdkSettings
)
.
build
();
imaAdsLoader
.
setPlayer
(
fakePlayer
);
adsMediaSource
=
new
AdsMediaSource
(
new
FakeMediaSource
(
CONTENT_TIMELINE
),
TEST_DATA_SPEC
,
TEST_ADS_ID
,
new
DefaultMediaSourceFactory
((
Context
)
getApplicationContext
()),
imaAdsLoader
,
adViewProvider
);
long
midrollWindowTimeUs
=
2
*
C
.
MICROS_PER_SECOND
;
long
midrollPeriodTimeUs
=
midrollWindowTimeUs
+
TimelineWindowDefinition
.
DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US
;
ImmutableList
<
Float
>
cuePoints
=
ImmutableList
.
of
(
0
f
,
(
float
)
midrollPeriodTimeUs
/
C
.
MICROS_PER_SECOND
);
setupPlayback
(
CONTENT_TIMELINE
,
cuePoints
,
new
ImaAdsLoader
.
Builder
(
getApplicationContext
())
.
setPlayAdBeforeStartPosition
(
false
)
.
setImaFactory
(
mockImaFactory
)
.
setImaSdkSettings
(
mockImaSdkSettings
)
.
build
(),
TEST_DATA_SPEC
,
TEST_ADS_ID
);
when
(
mockAdsManager
.
getAdCuePoints
()).
thenReturn
(
cuePoints
);
fakeExoPlayer
.
setPlayingContentPosition
(
C
.
usToMs
(
midrollWindowTimeUs
)
+
1_000
);
fakePlayer
.
setPlayingContentPosition
(
/* periodIndex= */
0
,
C
.
usToMs
(
midrollWindowTimeUs
)
+
1_000
);
imaAdsLoader
.
start
(
adsMediaSource
,
TEST_DATA_SPEC
,
TEST_ADS_ID
,
adViewProvider
,
adsLoaderListener
);
verify
(
mockAdsManager
).
destroy
();
assertThat
(
adsLoaderListener
.
adPlaybackState
)
assertThat
(
adsLoaderListener
.
getAdPlaybackState
(
/* periodIndex= */
0
)
)
.
isEqualTo
(
new
AdPlaybackState
(
TEST_ADS_ID
,
getAdGroupTimesUsForCuePoints
(
cuePoints
))
.
withContentDurationUs
(
CONTENT_PERIOD_DURATION_US
)
...
...
@@ -669,6 +700,21 @@ public final class ImaAdsLoaderTest {
@Test
public
void
resumePlaybackBeforeSecondMidroll_withoutPlayAdBeforeStartPosition_skipsFirstMidroll
()
{
imaAdsLoader
=
new
ImaAdsLoader
.
Builder
(
getApplicationContext
())
.
setPlayAdBeforeStartPosition
(
false
)
.
setImaFactory
(
mockImaFactory
)
.
setImaSdkSettings
(
mockImaSdkSettings
)
.
build
();
imaAdsLoader
.
setPlayer
(
fakePlayer
);
adsMediaSource
=
new
AdsMediaSource
(
new
FakeMediaSource
(
CONTENT_TIMELINE
),
TEST_DATA_SPEC
,
TEST_ADS_ID
,
new
DefaultMediaSourceFactory
((
Context
)
getApplicationContext
()),
imaAdsLoader
,
adViewProvider
);
long
firstMidrollWindowTimeUs
=
2
*
C
.
MICROS_PER_SECOND
;
long
firstMidrollPeriodTimeUs
=
firstMidrollWindowTimeUs
...
...
@@ -681,18 +727,10 @@ public final class ImaAdsLoaderTest {
ImmutableList
.
of
(
(
float
)
firstMidrollPeriodTimeUs
/
C
.
MICROS_PER_SECOND
,
(
float
)
secondMidrollPeriodTimeUs
/
C
.
MICROS_PER_SECOND
);
setupPlayback
(
CONTENT_TIMELINE
,
cuePoints
,
new
ImaAdsLoader
.
Builder
(
getApplicationContext
())
.
setPlayAdBeforeStartPosition
(
false
)
.
setImaFactory
(
mockImaFactory
)
.
setImaSdkSettings
(
mockImaSdkSettings
)
.
build
(),
TEST_DATA_SPEC
,
TEST_ADS_ID
);
when
(
mockAdsManager
.
getAdCuePoints
()).
thenReturn
(
cuePoints
);
fakeExoPlayer
.
setPlayingContentPosition
(
C
.
usToMs
(
secondMidrollWindowTimeUs
)
-
1_000
);
fakePlayer
.
setPlayingContentPosition
(
/* periodIndex= */
0
,
C
.
usToMs
(
secondMidrollWindowTimeUs
)
-
1_000
);
imaAdsLoader
.
start
(
adsMediaSource
,
TEST_DATA_SPEC
,
TEST_ADS_ID
,
adViewProvider
,
adsLoaderListener
);
...
...
@@ -702,7 +740,7 @@ public final class ImaAdsLoaderTest {
assertThat
(
playAdsAfterTimeCaptor
.
getValue
())
.
isWithin
(
0.1d
)
.
of
(
expectedPlayAdsAfterTimeUs
/
C
.
MICROS_PER_SECOND
);
assertThat
(
adsLoaderListener
.
adPlaybackState
)
assertThat
(
adsLoaderListener
.
getAdPlaybackState
(
/* periodIndex= */
0
)
)
.
isEqualTo
(
new
AdPlaybackState
(
TEST_ADS_ID
,
getAdGroupTimesUsForCuePoints
(
cuePoints
))
.
withSkippedAdGroup
(
/* adGroupIndex= */
0
)
...
...
@@ -711,6 +749,21 @@ public final class ImaAdsLoaderTest {
@Test
public
void
resumePlaybackAtSecondMidroll_withoutPlayAdBeforeStartPosition_skipsFirstMidroll
()
{
imaAdsLoader
=
new
ImaAdsLoader
.
Builder
(
getApplicationContext
())
.
setPlayAdBeforeStartPosition
(
false
)
.
setImaFactory
(
mockImaFactory
)
.
setImaSdkSettings
(
mockImaSdkSettings
)
.
build
();
imaAdsLoader
.
setPlayer
(
fakePlayer
);
adsMediaSource
=
new
AdsMediaSource
(
new
FakeMediaSource
(
CONTENT_TIMELINE
),
TEST_DATA_SPEC
,
TEST_ADS_ID
,
new
DefaultMediaSourceFactory
((
Context
)
getApplicationContext
()),
imaAdsLoader
,
adViewProvider
);
long
firstMidrollWindowTimeUs
=
2
*
C
.
MICROS_PER_SECOND
;
long
firstMidrollPeriodTimeUs
=
firstMidrollWindowTimeUs
...
...
@@ -723,18 +776,9 @@ public final class ImaAdsLoaderTest {
ImmutableList
.
of
(
(
float
)
firstMidrollPeriodTimeUs
/
C
.
MICROS_PER_SECOND
,
(
float
)
secondMidrollPeriodTimeUs
/
C
.
MICROS_PER_SECOND
);
setupPlayback
(
CONTENT_TIMELINE
,
cuePoints
,
new
ImaAdsLoader
.
Builder
(
getApplicationContext
())
.
setPlayAdBeforeStartPosition
(
false
)
.
setImaFactory
(
mockImaFactory
)
.
setImaSdkSettings
(
mockImaSdkSettings
)
.
build
(),
TEST_DATA_SPEC
,
TEST_ADS_ID
);
when
(
mockAdsManager
.
getAdCuePoints
()).
thenReturn
(
cuePoints
);
fake
ExoPlayer
.
setPlayingContentPosition
(
C
.
usToMs
(
secondMidrollWindowTimeUs
));
fake
Player
.
setPlayingContentPosition
(
/* periodIndex= */
0
,
C
.
usToMs
(
secondMidrollWindowTimeUs
));
imaAdsLoader
.
start
(
adsMediaSource
,
TEST_DATA_SPEC
,
TEST_ADS_ID
,
adViewProvider
,
adsLoaderListener
);
...
...
@@ -744,7 +788,7 @@ public final class ImaAdsLoaderTest {
assertThat
(
playAdsAfterTimeCaptor
.
getValue
())
.
isWithin
(
0.1d
)
.
of
(
expectedPlayAdsAfterTimeUs
/
C
.
MICROS_PER_SECOND
);
assertThat
(
adsLoaderListener
.
adPlaybackState
)
assertThat
(
adsLoaderListener
.
getAdPlaybackState
(
/* periodIndex= */
0
)
)
.
isEqualTo
(
new
AdPlaybackState
(
TEST_ADS_ID
,
getAdGroupTimesUsForCuePoints
(
cuePoints
))
.
withContentDurationUs
(
CONTENT_PERIOD_DURATION_US
)
...
...
@@ -762,16 +806,6 @@ public final class ImaAdsLoaderTest {
+
" </Ad>\n"
+
"</VAST>"
;
DataSpec
adDataSpec
=
new
DataSpec
(
Util
.
getDataUriForString
(
"text/xml"
,
adsResponse
));
setupPlayback
(
CONTENT_TIMELINE
,
ImmutableList
.
of
(
0
f
),
new
ImaAdsLoader
.
Builder
(
getApplicationContext
())
.
setImaFactory
(
mockImaFactory
)
.
setImaSdkSettings
(
mockImaSdkSettings
)
.
build
(),
adDataSpec
,
TEST_ADS_ID
);
imaAdsLoader
.
start
(
adsMediaSource
,
adDataSpec
,
TEST_ADS_ID
,
adViewProvider
,
adsLoaderListener
);
verify
(
mockAdsRequest
).
setAdsResponse
(
adsResponse
);
...
...
@@ -779,15 +813,6 @@ public final class ImaAdsLoaderTest {
@Test
public
void
requestAdTagWithUri_requestsWithAdTagUrl
()
throws
Exception
{
setupPlayback
(
CONTENT_TIMELINE
,
ImmutableList
.
of
(
0
f
),
new
ImaAdsLoader
.
Builder
(
getApplicationContext
())
.
setImaFactory
(
mockImaFactory
)
.
setImaSdkSettings
(
mockImaSdkSettings
)
.
build
(),
TEST_DATA_SPEC
,
TEST_ADS_ID
);
imaAdsLoader
.
start
(
adsMediaSource
,
TEST_DATA_SPEC
,
TEST_ADS_ID
,
adViewProvider
,
adsLoaderListener
);
...
...
@@ -796,7 +821,6 @@ public final class ImaAdsLoaderTest {
@Test
public
void
setsDefaultMimeTypes
()
throws
Exception
{
setupPlayback
(
CONTENT_TIMELINE
,
ImmutableList
.
of
(
0
f
));
imaAdsLoader
.
setSupportedContentTypes
(
C
.
TYPE_DASH
,
C
.
TYPE_OTHER
);
imaAdsLoader
.
start
(
adsMediaSource
,
TEST_DATA_SPEC
,
TEST_ADS_ID
,
adViewProvider
,
adsLoaderListener
);
...
...
@@ -814,16 +838,23 @@ public final class ImaAdsLoaderTest {
@Test
public
void
buildWithAdMediaMimeTypes_setsMimeTypes
()
throws
Exception
{
setupPlayback
(
CONTENT_TIMELINE
,
ImmutableList
.
of
(
0
f
),
imaAdsLoader
=
new
ImaAdsLoader
.
Builder
(
getApplicationContext
())
.
setAdMediaMimeTypes
(
ImmutableList
.
of
(
MimeTypes
.
AUDIO_MPEG
))
.
setImaFactory
(
mockImaFactory
)
.
setImaSdkSettings
(
mockImaSdkSettings
)
.
setAdMediaMimeTypes
(
ImmutableList
.
of
(
MimeTypes
.
AUDIO_MPEG
))
.
build
(),
TEST_DATA_SPEC
,
TEST_ADS_ID
);
.
build
();
imaAdsLoader
.
setPlayer
(
fakePlayer
);
adsMediaSource
=
new
AdsMediaSource
(
new
FakeMediaSource
(
CONTENT_TIMELINE
),
TEST_DATA_SPEC
,
TEST_ADS_ID
,
new
DefaultMediaSourceFactory
((
Context
)
getApplicationContext
()),
imaAdsLoader
,
adViewProvider
);
when
(
mockAdsManager
.
getAdCuePoints
()).
thenReturn
(
PREROLL_CUE_POINTS_SECONDS
);
imaAdsLoader
.
setSupportedContentTypes
(
C
.
TYPE_OTHER
);
imaAdsLoader
.
start
(
adsMediaSource
,
TEST_DATA_SPEC
,
TEST_ADS_ID
,
adViewProvider
,
adsLoaderListener
);
...
...
@@ -833,7 +864,6 @@ public final class ImaAdsLoaderTest {
@Test
public
void
stop_unregistersAllVideoControlOverlays
()
{
setupPlayback
(
CONTENT_TIMELINE
,
PREROLL_CUE_POINTS_SECONDS
);
imaAdsLoader
.
start
(
adsMediaSource
,
TEST_DATA_SPEC
,
TEST_ADS_ID
,
adViewProvider
,
adsLoaderListener
);
imaAdsLoader
.
requestAds
(
TEST_DATA_SPEC
,
TEST_ADS_ID
,
adViewGroup
);
...
...
@@ -848,8 +878,9 @@ public final class ImaAdsLoaderTest {
public
void
loadAd_withLargeAdCuePoint_updatesAdPlaybackStateWithLoadedAd
()
{
// Use a large enough value to test correct truncating of large cue points.
float
midrollTimeSecs
=
Float
.
MAX_VALUE
;
ImmutableList
<
Float
>
cuePoints
=
ImmutableList
.
of
(
midrollTimeSecs
);
setupPlayback
(
CONTENT_TIMELINE
,
cuePoints
);
List
<
Float
>
cuePoints
=
ImmutableList
.
of
(
midrollTimeSecs
);
when
(
mockAdsManager
.
getAdCuePoints
()).
thenReturn
(
cuePoints
);
imaAdsLoader
.
start
(
adsMediaSource
,
TEST_DATA_SPEC
,
TEST_ADS_ID
,
adViewProvider
,
adsLoaderListener
);
videoAdPlayer
.
loadAd
(
...
...
@@ -886,7 +917,7 @@ public final class ImaAdsLoaderTest {
}
});
assertThat
(
adsLoaderListener
.
adPlaybackState
)
assertThat
(
adsLoaderListener
.
getAdPlaybackState
(
/* periodIndex= */
0
)
)
.
isEqualTo
(
new
AdPlaybackState
(
TEST_ADS_ID
,
getAdGroupTimesUsForCuePoints
(
cuePoints
))
.
withContentDurationUs
(
CONTENT_PERIOD_DURATION_US
)
...
...
@@ -895,37 +926,126 @@ public final class ImaAdsLoaderTest {
.
withAdDurationsUs
(
new
long
[][]
{{
TEST_AD_DURATION_US
}}));
}
private
void
setupPlayback
(
Timeline
contentTimeline
,
List
<
Float
>
cuePoints
)
{
setupPlayback
(
contentTimeline
,
cuePoints
,
new
ImaAdsLoader
.
Builder
(
getApplicationContext
())
.
setImaFactory
(
mockImaFactory
)
.
setImaSdkSettings
(
mockImaSdkSettings
)
.
build
(),
TEST_DATA_SPEC
,
TEST_ADS_ID
);
@Test
public
void
playbackWithTwoAdsMediaSources_preloadsSecondAdTag
()
{
Object
secondAdsId
=
new
Object
();
AdsMediaSource
secondAdsMediaSource
=
new
AdsMediaSource
(
new
FakeMediaSource
(
CONTENT_TIMELINE
),
TEST_DATA_SPEC
,
secondAdsId
,
new
DefaultMediaSourceFactory
((
Context
)
getApplicationContext
()),
imaAdsLoader
,
adViewProvider
);
adsLoaderListener
=
new
TestAdsLoaderListener
(
getInitialTimelineWindowDefinition
(
TEST_ADS_ID
),
getInitialTimelineWindowDefinition
(
secondAdsId
));
// Load and play the preroll ad then content.
imaAdsLoader
.
start
(
adsMediaSource
,
TEST_DATA_SPEC
,
TEST_ADS_ID
,
adViewProvider
,
adsLoaderListener
);
adEventListener
.
onAdEvent
(
getAdEvent
(
AdEventType
.
LOADED
,
mockPrerollSingleAd
));
videoAdPlayer
.
loadAd
(
TEST_AD_MEDIA_INFO
,
mockAdPodInfo
);
adEventListener
.
onAdEvent
(
getAdEvent
(
AdEventType
.
CONTENT_PAUSE_REQUESTED
,
mockPrerollSingleAd
));
videoAdPlayer
.
playAd
(
TEST_AD_MEDIA_INFO
);
fakePlayer
.
setPlayingAdPosition
(
/* periodIndex= */
0
,
/* adGroupIndex= */
0
,
/* adIndexInAdGroup= */
0
,
/* position= */
0
,
/* contentPosition= */
0
);
fakePlayer
.
setState
(
Player
.
STATE_READY
,
true
);
adEventListener
.
onAdEvent
(
getAdEvent
(
AdEventType
.
STARTED
,
mockPrerollSingleAd
));
adEventListener
.
onAdEvent
(
getAdEvent
(
AdEventType
.
FIRST_QUARTILE
,
mockPrerollSingleAd
));
adEventListener
.
onAdEvent
(
getAdEvent
(
AdEventType
.
MIDPOINT
,
mockPrerollSingleAd
));
adEventListener
.
onAdEvent
(
getAdEvent
(
AdEventType
.
THIRD_QUARTILE
,
mockPrerollSingleAd
));
fakePlayer
.
setPlayingContentPosition
(
/* periodIndex= */
0
,
/* positionMs= */
0
);
videoAdPlayer
.
stopAd
(
TEST_AD_MEDIA_INFO
);
adEventListener
.
onAdEvent
(
getAdEvent
(
AdEventType
.
CONTENT_RESUME_REQUESTED
,
/* ad= */
null
));
// Simulate starting to buffer the second ads media source.
imaAdsLoader
.
start
(
secondAdsMediaSource
,
TEST_DATA_SPEC
,
secondAdsId
,
adViewProvider
,
adsLoaderListener
);
// Verify that the preroll ad has been marked as played.
assertThat
(
adsLoaderListener
.
getAdPlaybackState
(
/* periodIndex= */
0
))
.
isEqualTo
(
new
AdPlaybackState
(
TEST_ADS_ID
,
/* adGroupTimesUs...= */
0
)
.
withContentDurationUs
(
CONTENT_PERIOD_DURATION_US
)
.
withAdCount
(
/* adGroupIndex= */
0
,
/* adCount= */
1
)
.
withAdUri
(
/* adGroupIndex= */
0
,
/* adIndexInAdGroup= */
0
,
TEST_URI
)
.
withAdDurationsUs
(
new
long
[][]
{{
TEST_AD_DURATION_US
}})
.
withPlayedAd
(
/* adGroupIndex= */
0
,
/* adIndexInAdGroup= */
0
)
.
withAdResumePositionUs
(
/* adResumePositionUs= */
0
));
// Verify that the second source's ad cue points have preloaded.
assertThat
(
adsLoaderListener
.
getAdPlaybackState
(
/* periodIndex= */
1
))
.
isEqualTo
(
new
AdPlaybackState
(
secondAdsId
,
/* adGroupTimesUs...= */
0
));
}
private
void
setupPlayback
(
Timeline
contentTimeline
,
List
<
Float
>
cuePoints
,
ImaAdsLoader
imaAdsLoader
,
DataSpec
adTagDataSpec
,
Object
adsId
)
{
fakeExoPlayer
=
new
FakePlayer
();
adsLoaderListener
=
new
TestAdsLoaderListener
(
fakeExoPlayer
,
contentTimeline
);
adsMediaSource
=
@Test
public
void
playbackWithTwoAdsMediaSources_preloadsSecondAdTagWithBackgroundResume
()
{
Object
secondAdsId
=
new
Object
();
AdsMediaSource
secondAdsMediaSource
=
new
AdsMediaSource
(
new
FakeMediaSource
(
new
FakeTimeline
(
/* windowCount= */
1
)
),
adTagDataSpec
,
a
dsId
,
new
FakeMediaSource
(
CONTENT_TIMELINE
),
TEST_DATA_SPEC
,
secondA
dsId
,
new
DefaultMediaSourceFactory
((
Context
)
getApplicationContext
()),
imaAdsLoader
,
adViewProvider
);
when
(
mockAdsManager
.
getAdCuePoints
()).
thenReturn
(
cuePoints
);
this
.
imaAdsLoader
=
imaAdsLoader
;
imaAdsLoader
.
setPlayer
(
fakeExoPlayer
);
adsLoaderListener
=
new
TestAdsLoaderListener
(
getInitialTimelineWindowDefinition
(
TEST_ADS_ID
),
getInitialTimelineWindowDefinition
(
secondAdsId
));
// Load and play the preroll ad then content.
imaAdsLoader
.
start
(
adsMediaSource
,
TEST_DATA_SPEC
,
TEST_ADS_ID
,
adViewProvider
,
adsLoaderListener
);
adEventListener
.
onAdEvent
(
getAdEvent
(
AdEventType
.
LOADED
,
mockPrerollSingleAd
));
videoAdPlayer
.
loadAd
(
TEST_AD_MEDIA_INFO
,
mockAdPodInfo
);
adEventListener
.
onAdEvent
(
getAdEvent
(
AdEventType
.
CONTENT_PAUSE_REQUESTED
,
mockPrerollSingleAd
));
videoAdPlayer
.
playAd
(
TEST_AD_MEDIA_INFO
);
fakePlayer
.
setPlayingAdPosition
(
/* periodIndex= */
0
,
/* adGroupIndex= */
0
,
/* adIndexInAdGroup= */
0
,
/* position= */
0
,
/* contentPosition= */
0
);
fakePlayer
.
setState
(
Player
.
STATE_READY
,
true
);
adEventListener
.
onAdEvent
(
getAdEvent
(
AdEventType
.
STARTED
,
mockPrerollSingleAd
));
adEventListener
.
onAdEvent
(
getAdEvent
(
AdEventType
.
FIRST_QUARTILE
,
mockPrerollSingleAd
));
adEventListener
.
onAdEvent
(
getAdEvent
(
AdEventType
.
MIDPOINT
,
mockPrerollSingleAd
));
adEventListener
.
onAdEvent
(
getAdEvent
(
AdEventType
.
THIRD_QUARTILE
,
mockPrerollSingleAd
));
fakePlayer
.
setPlayingContentPosition
(
/* periodIndex= */
0
,
/* positionMs= */
0
);
videoAdPlayer
.
stopAd
(
TEST_AD_MEDIA_INFO
);
adEventListener
.
onAdEvent
(
getAdEvent
(
AdEventType
.
CONTENT_RESUME_REQUESTED
,
/* ad= */
null
));
// Simulate starting to buffer the second ads media source.
imaAdsLoader
.
start
(
secondAdsMediaSource
,
TEST_DATA_SPEC
,
secondAdsId
,
adViewProvider
,
adsLoaderListener
);
// Simulate backgrounding/resuming.
imaAdsLoader
.
stop
(
adsMediaSource
);
imaAdsLoader
.
stop
(
secondAdsMediaSource
);
imaAdsLoader
.
start
(
adsMediaSource
,
TEST_DATA_SPEC
,
TEST_ADS_ID
,
adViewProvider
,
adsLoaderListener
);
imaAdsLoader
.
start
(
secondAdsMediaSource
,
TEST_DATA_SPEC
,
secondAdsId
,
adViewProvider
,
adsLoaderListener
);
// Verify that the preroll ad has been marked as played.
assertThat
(
adsLoaderListener
.
getAdPlaybackState
(
/* periodIndex= */
0
))
.
isEqualTo
(
new
AdPlaybackState
(
TEST_ADS_ID
,
/* adGroupTimesUs...= */
0
)
.
withContentDurationUs
(
CONTENT_PERIOD_DURATION_US
)
.
withAdCount
(
/* adGroupIndex= */
0
,
/* adCount= */
1
)
.
withAdUri
(
/* adGroupIndex= */
0
,
/* adIndexInAdGroup= */
0
,
TEST_URI
)
.
withAdDurationsUs
(
new
long
[][]
{{
TEST_AD_DURATION_US
}})
.
withPlayedAd
(
/* adGroupIndex= */
0
,
/* adIndexInAdGroup= */
0
)
.
withAdResumePositionUs
(
/* adResumePositionUs= */
0
));
// Verify that the second source's ad cue points have preloaded.
assertThat
(
adsLoaderListener
.
getAdPlaybackState
(
/* periodIndex= */
1
))
.
isEqualTo
(
new
AdPlaybackState
(
secondAdsId
,
/* adGroupTimesUs...= */
0
));
}
private
void
setupMocks
()
{
...
...
@@ -1023,16 +1143,16 @@ public final class ImaAdsLoaderTest {
}
/** Ad loader event listener that forwards ad playback state to a fake player. */
private
static
final
class
TestAdsLoaderListener
implements
AdsLoader
.
EventListener
{
private
final
class
TestAdsLoaderListener
implements
AdsLoader
.
EventListener
{
private
final
FakePlayer
fakeExoPlayer
;
private
final
Timeline
contentTimeline
;
private
final
TimelineWindowDefinition
[]
timelineWindowDefinitions
;
public
AdPlaybackState
adPlaybackState
;
public
TestAdsLoaderListener
(
TimelineWindowDefinition
...
timelineWindowDefinitions
)
{
this
.
timelineWindowDefinitions
=
timelineWindowDefinitions
;
}
public
TestAdsLoaderListener
(
FakePlayer
fakeExoPlayer
,
Timeline
contentTimeline
)
{
this
.
fakeExoPlayer
=
fakeExoPlayer
;
this
.
contentTimeline
=
contentTimeline
;
public
AdPlaybackState
getAdPlaybackState
(
int
periodIndex
)
{
return
timelineWindowDefinitions
[
periodIndex
].
adPlaybackState
;
}
@Override
...
...
@@ -1043,10 +1163,28 @@ public final class ImaAdsLoaderTest {
Arrays
.
fill
(
adDurationsUs
[
adGroupIndex
],
TEST_AD_DURATION_US
);
}
adPlaybackState
=
adPlaybackState
.
withAdDurationsUs
(
adDurationsUs
);
this
.
adPlaybackState
=
adPlaybackState
;
fakeExoPlayer
.
updateTimeline
(
new
SinglePeriodAdTimeline
(
contentTimeline
,
adPlaybackState
),
Player
.
TIMELINE_CHANGE_REASON_SOURCE_UPDATE
);
// Update the timeline window definition(s) to reflect the new ad playback state.
for
(
int
i
=
0
;
i
<
timelineWindowDefinitions
.
length
;
i
++)
{
TimelineWindowDefinition
timelineWindowDefinition
=
timelineWindowDefinitions
[
i
];
if
(!
Util
.
areEqual
(
timelineWindowDefinition
.
adPlaybackState
.
adsId
,
adPlaybackState
.
adsId
))
{
continue
;
}
timelineWindowDefinitions
[
i
]
=
new
TimelineWindowDefinition
(
timelineWindowDefinition
.
periodCount
,
timelineWindowDefinition
.
id
,
timelineWindowDefinition
.
isSeekable
,
timelineWindowDefinition
.
isDynamic
,
timelineWindowDefinition
.
isLive
,
timelineWindowDefinition
.
isPlaceholder
,
timelineWindowDefinition
.
durationUs
,
timelineWindowDefinition
.
defaultPositionUs
,
timelineWindowDefinition
.
windowOffsetInFirstPeriodUs
,
adPlaybackState
);
}
fakePlayer
.
updateTimeline
(
new
FakeTimeline
(
timelineWindowDefinitions
),
Player
.
TIMELINE_CHANGE_REASON_SOURCE_UPDATE
);
}
@Override
...
...
@@ -1064,4 +1202,24 @@ public final class ImaAdsLoaderTest {
// Do nothing.
}
}
private
static
TimelineWindowDefinition
getInitialTimelineWindowDefinition
(
Object
adsId
)
{
return
getInitialTimelineWindowDefinition
(
adsId
,
/* isPlaceholder= */
false
);
}
private
static
TimelineWindowDefinition
getInitialTimelineWindowDefinition
(
Object
adsId
,
boolean
isPlaceholder
)
{
return
new
TimelineWindowDefinition
(
/* periodCount= */
1
,
/* id= */
new
Object
(),
/* isSeekable= */
true
,
/* isDynamic= */
false
,
/* isLive= */
false
,
/* isPlaceholder= */
isPlaceholder
,
/* durationUs= */
CONTENT_DURATION_US
,
/* defaultPositionUs= */
0
,
/* windowOffsetInFirstPeriodUs= */
TimelineWindowDefinition
.
DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US
,
new
AdPlaybackState
(
adsId
));
}
}
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