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
3b99a84d
authored
May 05, 2020
by
andrewlewis
Committed by
Andrew Lewis
May 29, 2020
Browse files
Options
_('Browse Files')
Download
Email Patches
Plain Diff
Migrate to new IMA preloading APIs
issue:#6429 PiperOrigin-RevId: 309906760
parent
de03e389
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
289 additions
and
224 deletions
RELEASENOTES.md
extensions/ima/build.gradle
extensions/ima/src/main/java/com/google/android/exoplayer2/ext/ima/ImaAdsLoader.java
extensions/ima/src/test/java/com/google/android/exoplayer2/ext/ima/ImaAdsLoaderTest.java
RELEASENOTES.md
View file @
3b99a84d
...
@@ -52,6 +52,8 @@
...
@@ -52,6 +52,8 @@
(
[
#7234
](
https://github.com/google/ExoPlayer/issues/7234
)
).
(
[
#7234
](
https://github.com/google/ExoPlayer/issues/7234
)
).
*
AV1 extension: Add a heuristic to determine the default number of threads
*
AV1 extension: Add a heuristic to determine the default number of threads
used for AV1 playback using the extension.
used for AV1 playback using the extension.
*
IMA extension: Upgrade to IMA SDK version 3.18.1, and migrate to new
preloading APIs (
[
#6429
](
https://github.com/google/ExoPlayer/issues/6429
)
).
### 2.11.4 (2020-04-08) ###
### 2.11.4 (2020-04-08) ###
...
...
extensions/ima/build.gradle
View file @
3b99a84d
...
@@ -32,7 +32,7 @@ android {
...
@@ -32,7 +32,7 @@ android {
}
}
dependencies
{
dependencies
{
api
'com.google.ads.interactivemedia.v3:interactivemedia:3.1
1.3
'
api
'com.google.ads.interactivemedia.v3:interactivemedia:3.1
8.1
'
implementation
project
(
modulePrefix
+
'library-core'
)
implementation
project
(
modulePrefix
+
'library-core'
)
implementation
'androidx.annotation:annotation:'
+
androidxAnnotationVersion
implementation
'androidx.annotation:annotation:'
+
androidxAnnotationVersion
implementation
'com.google.android.gms:play-services-ads-identifier:17.0.0'
implementation
'com.google.android.gms:play-services-ads-identifier:17.0.0'
...
...
extensions/ima/src/main/java/com/google/android/exoplayer2/ext/ima/ImaAdsLoader.java
View file @
3b99a84d
...
@@ -19,6 +19,7 @@ import static com.google.android.exoplayer2.util.Util.castNonNull;
...
@@ -19,6 +19,7 @@ import static com.google.android.exoplayer2.util.Util.castNonNull;
import
android.content.Context
;
import
android.content.Context
;
import
android.net.Uri
;
import
android.net.Uri
;
import
android.os.Handler
;
import
android.os.Looper
;
import
android.os.Looper
;
import
android.os.SystemClock
;
import
android.os.SystemClock
;
import
android.view.View
;
import
android.view.View
;
...
@@ -26,7 +27,6 @@ import android.view.ViewGroup;
...
@@ -26,7 +27,6 @@ import android.view.ViewGroup;
import
androidx.annotation.IntDef
;
import
androidx.annotation.IntDef
;
import
androidx.annotation.Nullable
;
import
androidx.annotation.Nullable
;
import
androidx.annotation.VisibleForTesting
;
import
androidx.annotation.VisibleForTesting
;
import
com.google.ads.interactivemedia.v3.api.Ad
;
import
com.google.ads.interactivemedia.v3.api.AdDisplayContainer
;
import
com.google.ads.interactivemedia.v3.api.AdDisplayContainer
;
import
com.google.ads.interactivemedia.v3.api.AdError
;
import
com.google.ads.interactivemedia.v3.api.AdError
;
import
com.google.ads.interactivemedia.v3.api.AdError.AdErrorCode
;
import
com.google.ads.interactivemedia.v3.api.AdError.AdErrorCode
;
...
@@ -45,6 +45,7 @@ import com.google.ads.interactivemedia.v3.api.CompanionAdSlot;
...
@@ -45,6 +45,7 @@ import com.google.ads.interactivemedia.v3.api.CompanionAdSlot;
import
com.google.ads.interactivemedia.v3.api.ImaSdkFactory
;
import
com.google.ads.interactivemedia.v3.api.ImaSdkFactory
;
import
com.google.ads.interactivemedia.v3.api.ImaSdkSettings
;
import
com.google.ads.interactivemedia.v3.api.ImaSdkSettings
;
import
com.google.ads.interactivemedia.v3.api.UiElement
;
import
com.google.ads.interactivemedia.v3.api.UiElement
;
import
com.google.ads.interactivemedia.v3.api.player.AdMediaInfo
;
import
com.google.ads.interactivemedia.v3.api.player.ContentProgressProvider
;
import
com.google.ads.interactivemedia.v3.api.player.ContentProgressProvider
;
import
com.google.ads.interactivemedia.v3.api.player.VideoAdPlayer
;
import
com.google.ads.interactivemedia.v3.api.player.VideoAdPlayer
;
import
com.google.ads.interactivemedia.v3.api.player.VideoProgressUpdate
;
import
com.google.ads.interactivemedia.v3.api.player.VideoProgressUpdate
;
...
@@ -54,7 +55,6 @@ import com.google.android.exoplayer2.ExoPlayerLibraryInfo;
...
@@ -54,7 +55,6 @@ import com.google.android.exoplayer2.ExoPlayerLibraryInfo;
import
com.google.android.exoplayer2.Player
;
import
com.google.android.exoplayer2.Player
;
import
com.google.android.exoplayer2.Timeline
;
import
com.google.android.exoplayer2.Timeline
;
import
com.google.android.exoplayer2.source.ads.AdPlaybackState
;
import
com.google.android.exoplayer2.source.ads.AdPlaybackState
;
import
com.google.android.exoplayer2.source.ads.AdPlaybackState.AdState
;
import
com.google.android.exoplayer2.source.ads.AdsLoader
;
import
com.google.android.exoplayer2.source.ads.AdsLoader
;
import
com.google.android.exoplayer2.source.ads.AdsMediaSource.AdLoadException
;
import
com.google.android.exoplayer2.source.ads.AdsMediaSource.AdLoadException
;
import
com.google.android.exoplayer2.trackselection.TrackSelectionArray
;
import
com.google.android.exoplayer2.trackselection.TrackSelectionArray
;
...
@@ -71,6 +71,7 @@ import java.util.ArrayList;
...
@@ -71,6 +71,7 @@ import java.util.ArrayList;
import
java.util.Arrays
;
import
java.util.Arrays
;
import
java.util.Collection
;
import
java.util.Collection
;
import
java.util.Collections
;
import
java.util.Collections
;
import
java.util.HashMap
;
import
java.util.HashSet
;
import
java.util.HashSet
;
import
java.util.List
;
import
java.util.List
;
import
java.util.Map
;
import
java.util.Map
;
...
@@ -277,6 +278,14 @@ public final class ImaAdsLoader
...
@@ -277,6 +278,14 @@ public final class ImaAdsLoader
private
static
final
String
IMA_SDK_SETTINGS_PLAYER_TYPE
=
"google/exo.ext.ima"
;
private
static
final
String
IMA_SDK_SETTINGS_PLAYER_TYPE
=
"google/exo.ext.ima"
;
private
static
final
String
IMA_SDK_SETTINGS_PLAYER_VERSION
=
ExoPlayerLibraryInfo
.
VERSION
;
private
static
final
String
IMA_SDK_SETTINGS_PLAYER_VERSION
=
ExoPlayerLibraryInfo
.
VERSION
;
/**
* Interval at which ad progress updates are provided to the IMA SDK, in milliseconds. 100 ms is
* the interval recommended by the IMA documentation.
*
* @see com.google.ads.interactivemedia.v3.api.player.VideoAdPlayer.VideoAdPlayerCallback
*/
private
static
final
int
AD_PROGRESS_UPDATE_INTERVAL_MS
=
100
;
/** The value used in {@link VideoProgressUpdate}s to indicate an unset duration. */
/** The value used in {@link VideoProgressUpdate}s to indicate an unset duration. */
private
static
final
long
IMA_DURATION_UNSET
=
-
1L
;
private
static
final
long
IMA_DURATION_UNSET
=
-
1L
;
...
@@ -286,9 +295,6 @@ public final class ImaAdsLoader
...
@@ -286,9 +295,6 @@ public final class ImaAdsLoader
*/
*/
private
static
final
long
END_OF_CONTENT_THRESHOLD_MS
=
5000
;
private
static
final
long
END_OF_CONTENT_THRESHOLD_MS
=
5000
;
/** The maximum duration before an ad break that IMA may start preloading the next ad. */
private
static
final
long
MAXIMUM_PRELOAD_DURATION_MS
=
8000
;
private
static
final
int
TIMEOUT_UNSET
=
-
1
;
private
static
final
int
TIMEOUT_UNSET
=
-
1
;
private
static
final
int
BITRATE_UNSET
=
-
1
;
private
static
final
int
BITRATE_UNSET
=
-
1
;
...
@@ -302,11 +308,12 @@ public final class ImaAdsLoader
...
@@ -302,11 +308,12 @@ public final class ImaAdsLoader
*/
*/
private
static
final
int
IMA_AD_STATE_NONE
=
0
;
private
static
final
int
IMA_AD_STATE_NONE
=
0
;
/**
/**
* The ad playback state when IMA has called {@link #playAd()} and not {@link #pauseAd()}.
* The ad playback state when IMA has called {@link #playAd(AdMediaInfo)} and not {@link
* #pauseAd(AdMediaInfo)}.
*/
*/
private
static
final
int
IMA_AD_STATE_PLAYING
=
1
;
private
static
final
int
IMA_AD_STATE_PLAYING
=
1
;
/**
/**
* The ad playback state when IMA has called {@link #pauseAd()} while playing an ad.
* The ad playback state when IMA has called {@link #pauseAd(
AdMediaInfo
)} while playing an ad.
*/
*/
private
static
final
int
IMA_AD_STATE_PAUSED
=
2
;
private
static
final
int
IMA_AD_STATE_PAUSED
=
2
;
...
@@ -320,10 +327,12 @@ public final class ImaAdsLoader
...
@@ -320,10 +327,12 @@ public final class ImaAdsLoader
@Nullable
private
final
AdEventListener
adEventListener
;
@Nullable
private
final
AdEventListener
adEventListener
;
private
final
ImaFactory
imaFactory
;
private
final
ImaFactory
imaFactory
;
private
final
Timeline
.
Period
period
;
private
final
Timeline
.
Period
period
;
private
final
Timeline
.
Window
window
;
private
final
Handler
handler
;
private
final
List
<
VideoAdPlayerCallback
>
adCallbacks
;
private
final
List
<
VideoAdPlayerCallback
>
adCallbacks
;
private
final
AdDisplayContainer
adDisplayContainer
;
private
final
AdDisplayContainer
adDisplayContainer
;
private
final
com
.
google
.
ads
.
interactivemedia
.
v3
.
api
.
AdsLoader
adsLoader
;
private
final
com
.
google
.
ads
.
interactivemedia
.
v3
.
api
.
AdsLoader
adsLoader
;
private
final
Runnable
updateAdProgressRunnable
;
private
final
Map
<
AdMediaInfo
,
AdInfo
>
adInfoByAdMediaInfo
;
private
boolean
wasSetPlayerCalled
;
private
boolean
wasSetPlayerCalled
;
@Nullable
private
Player
nextPlayer
;
@Nullable
private
Player
nextPlayer
;
...
@@ -341,19 +350,18 @@ public final class ImaAdsLoader
...
@@ -341,19 +350,18 @@ public final class ImaAdsLoader
@Nullable
private
AdLoadException
pendingAdLoadError
;
@Nullable
private
AdLoadException
pendingAdLoadError
;
private
Timeline
timeline
;
private
Timeline
timeline
;
private
long
contentDurationMs
;
private
long
contentDurationMs
;
private
int
podIndexOffset
;
private
AdPlaybackState
adPlaybackState
;
private
AdPlaybackState
adPlaybackState
;
// Fields tracking IMA's state.
// Fields tracking IMA's state.
/** The expected ad group index that IMA should load next. */
private
int
expectedAdGroupIndex
;
/** The index of the current ad group that IMA is loading. */
private
int
adGroupIndex
;
/** Whether IMA has sent an ad event to pause content since the last resume content event. */
/** Whether IMA has sent an ad event to pause content since the last resume content event. */
private
boolean
imaPausedContent
;
private
boolean
imaPausedContent
;
/** The current ad playback state. */
/** The current ad playback state. */
private
@ImaAdState
int
imaAdState
;
private
@ImaAdState
int
imaAdState
;
/** The current ad media info, or {@code null} if in state {@link #IMA_AD_STATE_NONE}. */
@Nullable
private
AdMediaInfo
imaAdMediaInfo
;
/** The current ad info, or {@code null} if in state {@link #IMA_AD_STATE_NONE}. */
@Nullable
private
AdInfo
imaAdInfo
;
/**
/**
* Whether {@link com.google.ads.interactivemedia.v3.api.AdsLoader#contentComplete()} has been
* Whether {@link com.google.ads.interactivemedia.v3.api.AdsLoader#contentComplete()} has been
* called since starting ad playback.
* called since starting ad playback.
...
@@ -364,20 +372,23 @@ public final class ImaAdsLoader
...
@@ -364,20 +372,23 @@ public final class ImaAdsLoader
/** Whether the player is playing an ad. */
/** Whether the player is playing an ad. */
private
boolean
playingAd
;
private
boolean
playingAd
;
/** Whether the player is buffering an ad. */
private
boolean
bufferingAd
;
/**
/**
* If the player is playing an ad, stores the ad index in its ad group. {@link C#INDEX_UNSET}
* If the player is playing an ad, stores the ad index in its ad group. {@link C#INDEX_UNSET}
* otherwise.
* otherwise.
*/
*/
private
int
playingAdIndexInAdGroup
;
private
int
playingAdIndexInAdGroup
;
/**
/**
*
Whether there's a pending ad preparation error which IMA needs to be notified of when it
*
The ad info for a pending ad for which the media failed preparation, or {@code null} if no
*
transitions from playing content to playing the ad
.
*
pending ads have failed to prepare
.
*/
*/
private
boolean
shouldNotifyAdPrepareError
;
@Nullable
private
AdInfo
pendingAdPrepareErrorAdInfo
;
/**
/**
* If a content period has finished but IMA has not yet called {@link #playAd()}, stores the value
* If a content period has finished but IMA has not yet called {@link #playAd(AdMediaInfo)},
* of {@link SystemClock#elapsedRealtime()} when the content stopped playing. This can be used to
* stores the value of {@link SystemClock#elapsedRealtime()} when the content stopped playing.
* determine a fake, increasing content position. {@link C#TIME_UNSET} otherwise.
* This can be used to determine a fake, increasing content position. {@link C#TIME_UNSET}
* otherwise.
*/
*/
private
long
fakeContentProgressElapsedRealtimeMs
;
private
long
fakeContentProgressElapsedRealtimeMs
;
/**
/**
...
@@ -442,7 +453,7 @@ public final class ImaAdsLoader
...
@@ -442,7 +453,7 @@ public final class ImaAdsLoader
/* imaFactory= */
new
DefaultImaFactory
());
/* imaFactory= */
new
DefaultImaFactory
());
}
}
@SuppressWarnings
(
"nullness:argument.type.incompatible"
)
@SuppressWarnings
(
{
"nullness:argument.type.incompatible"
,
"methodref.receiver.bound.invalid"
}
)
private
ImaAdsLoader
(
private
ImaAdsLoader
(
Context
context
,
Context
context
,
@Nullable
Uri
adTagUri
,
@Nullable
Uri
adTagUri
,
...
@@ -474,7 +485,7 @@ public final class ImaAdsLoader
...
@@ -474,7 +485,7 @@ public final class ImaAdsLoader
imaSdkSettings
.
setPlayerType
(
IMA_SDK_SETTINGS_PLAYER_TYPE
);
imaSdkSettings
.
setPlayerType
(
IMA_SDK_SETTINGS_PLAYER_TYPE
);
imaSdkSettings
.
setPlayerVersion
(
IMA_SDK_SETTINGS_PLAYER_VERSION
);
imaSdkSettings
.
setPlayerVersion
(
IMA_SDK_SETTINGS_PLAYER_VERSION
);
period
=
new
Timeline
.
Period
();
period
=
new
Timeline
.
Period
();
window
=
new
Timeline
.
Window
(
);
handler
=
Util
.
createHandler
(
getImaLooper
(),
/* callback= */
null
);
adCallbacks
=
new
ArrayList
<>(
/* initialCapacity= */
1
);
adCallbacks
=
new
ArrayList
<>(
/* initialCapacity= */
1
);
adDisplayContainer
=
imaFactory
.
createAdDisplayContainer
();
adDisplayContainer
=
imaFactory
.
createAdDisplayContainer
();
adDisplayContainer
.
setPlayer
(
/* videoAdPlayer= */
this
);
adDisplayContainer
.
setPlayer
(
/* videoAdPlayer= */
this
);
...
@@ -483,13 +494,14 @@ public final class ImaAdsLoader
...
@@ -483,13 +494,14 @@ public final class ImaAdsLoader
context
.
getApplicationContext
(),
imaSdkSettings
,
adDisplayContainer
);
context
.
getApplicationContext
(),
imaSdkSettings
,
adDisplayContainer
);
adsLoader
.
addAdErrorListener
(
/* adErrorListener= */
this
);
adsLoader
.
addAdErrorListener
(
/* adErrorListener= */
this
);
adsLoader
.
addAdsLoadedListener
(
/* adsLoadedListener= */
this
);
adsLoader
.
addAdsLoadedListener
(
/* adsLoadedListener= */
this
);
updateAdProgressRunnable
=
this
::
updateAdProgress
;
adInfoByAdMediaInfo
=
new
HashMap
<>();
supportedMimeTypes
=
Collections
.
emptyList
();
supportedMimeTypes
=
Collections
.
emptyList
();
lastContentProgress
=
VideoProgressUpdate
.
VIDEO_TIME_NOT_READY
;
lastContentProgress
=
VideoProgressUpdate
.
VIDEO_TIME_NOT_READY
;
lastAdProgress
=
VideoProgressUpdate
.
VIDEO_TIME_NOT_READY
;
lastAdProgress
=
VideoProgressUpdate
.
VIDEO_TIME_NOT_READY
;
fakeContentProgressElapsedRealtimeMs
=
C
.
TIME_UNSET
;
fakeContentProgressElapsedRealtimeMs
=
C
.
TIME_UNSET
;
fakeContentProgressOffsetMs
=
C
.
TIME_UNSET
;
fakeContentProgressOffsetMs
=
C
.
TIME_UNSET
;
pendingContentPositionMs
=
C
.
TIME_UNSET
;
pendingContentPositionMs
=
C
.
TIME_UNSET
;
adGroupIndex
=
C
.
INDEX_UNSET
;
contentDurationMs
=
C
.
TIME_UNSET
;
contentDurationMs
=
C
.
TIME_UNSET
;
timeline
=
Timeline
.
EMPTY
;
timeline
=
Timeline
.
EMPTY
;
adPlaybackState
=
AdPlaybackState
.
NONE
;
adPlaybackState
=
AdPlaybackState
.
NONE
;
...
@@ -564,9 +576,8 @@ public final class ImaAdsLoader
...
@@ -564,9 +576,8 @@ public final class ImaAdsLoader
@Override
@Override
public
void
setPlayer
(
@Nullable
Player
player
)
{
public
void
setPlayer
(
@Nullable
Player
player
)
{
Assertions
.
checkState
(
Looper
.
getMainLooper
()
==
Looper
.
myLooper
());
Assertions
.
checkState
(
Looper
.
myLooper
()
==
getImaLooper
());
Assertions
.
checkState
(
Assertions
.
checkState
(
player
==
null
||
player
.
getApplicationLooper
()
==
getImaLooper
());
player
==
null
||
player
.
getApplicationLooper
()
==
Looper
.
getMainLooper
());
nextPlayer
=
player
;
nextPlayer
=
player
;
wasSetPlayerCalled
=
true
;
wasSetPlayerCalled
=
true
;
}
}
...
@@ -642,7 +653,7 @@ public final class ImaAdsLoader
...
@@ -642,7 +653,7 @@ public final class ImaAdsLoader
playingAd
?
C
.
msToUs
(
player
.
getCurrentPosition
())
:
0
);
playingAd
?
C
.
msToUs
(
player
.
getCurrentPosition
())
:
0
);
}
}
lastVolumePercentage
=
getVolume
();
lastVolumePercentage
=
getVolume
();
lastAdProgress
=
getAd
Progress
();
lastAdProgress
=
getAd
VideoProgressUpdate
();
lastContentProgress
=
getContentProgress
();
lastContentProgress
=
getContentProgress
();
adDisplayContainer
.
unregisterAllVideoControlsOverlays
();
adDisplayContainer
.
unregisterAllVideoControlsOverlays
();
player
.
removeListener
(
this
);
player
.
removeListener
(
this
);
...
@@ -666,6 +677,8 @@ public final class ImaAdsLoader
...
@@ -666,6 +677,8 @@ public final class ImaAdsLoader
adsLoader
.
removeAdErrorListener
(
/* adErrorListener= */
this
);
adsLoader
.
removeAdErrorListener
(
/* adErrorListener= */
this
);
imaPausedContent
=
false
;
imaPausedContent
=
false
;
imaAdState
=
IMA_AD_STATE_NONE
;
imaAdState
=
IMA_AD_STATE_NONE
;
imaAdMediaInfo
=
null
;
imaAdInfo
=
null
;
pendingAdLoadError
=
null
;
pendingAdLoadError
=
null
;
adPlaybackState
=
AdPlaybackState
.
NONE
;
adPlaybackState
=
AdPlaybackState
.
NONE
;
hasAdPlaybackState
=
false
;
hasAdPlaybackState
=
false
;
...
@@ -770,32 +783,11 @@ public final class ImaAdsLoader
...
@@ -770,32 +783,11 @@ public final class ImaAdsLoader
if
(
pendingContentPositionMs
!=
C
.
TIME_UNSET
)
{
if
(
pendingContentPositionMs
!=
C
.
TIME_UNSET
)
{
sentPendingContentPositionMs
=
true
;
sentPendingContentPositionMs
=
true
;
contentPositionMs
=
pendingContentPositionMs
;
contentPositionMs
=
pendingContentPositionMs
;
expectedAdGroupIndex
=
adPlaybackState
.
getAdGroupIndexForPositionUs
(
C
.
msToUs
(
contentPositionMs
),
C
.
msToUs
(
contentDurationMs
));
}
else
if
(
fakeContentProgressElapsedRealtimeMs
!=
C
.
TIME_UNSET
)
{
}
else
if
(
fakeContentProgressElapsedRealtimeMs
!=
C
.
TIME_UNSET
)
{
long
elapsedSinceEndMs
=
SystemClock
.
elapsedRealtime
()
-
fakeContentProgressElapsedRealtimeMs
;
long
elapsedSinceEndMs
=
SystemClock
.
elapsedRealtime
()
-
fakeContentProgressElapsedRealtimeMs
;
contentPositionMs
=
fakeContentProgressOffsetMs
+
elapsedSinceEndMs
;
contentPositionMs
=
fakeContentProgressOffsetMs
+
elapsedSinceEndMs
;
expectedAdGroupIndex
=
adPlaybackState
.
getAdGroupIndexForPositionUs
(
C
.
msToUs
(
contentPositionMs
),
C
.
msToUs
(
contentDurationMs
));
}
else
if
(
imaAdState
==
IMA_AD_STATE_NONE
&&
!
playingAd
&&
hasContentDuration
)
{
}
else
if
(
imaAdState
==
IMA_AD_STATE_NONE
&&
!
playingAd
&&
hasContentDuration
)
{
contentPositionMs
=
getContentPeriodPositionMs
(
player
,
timeline
,
period
);
contentPositionMs
=
getContentPeriodPositionMs
(
player
,
timeline
,
period
);
// Update the expected ad group index for the current content position. The update is delayed
// until MAXIMUM_PRELOAD_DURATION_MS before the ad so that an ad group load error delivered
// just after an ad group isn't incorrectly attributed to the next ad group.
int
nextAdGroupIndex
=
adPlaybackState
.
getAdGroupIndexAfterPositionUs
(
C
.
msToUs
(
contentPositionMs
),
C
.
msToUs
(
contentDurationMs
));
if
(
nextAdGroupIndex
!=
expectedAdGroupIndex
&&
nextAdGroupIndex
!=
C
.
INDEX_UNSET
)
{
long
nextAdGroupTimeMs
=
C
.
usToMs
(
adPlaybackState
.
adGroupTimesUs
[
nextAdGroupIndex
]);
if
(
nextAdGroupTimeMs
==
C
.
TIME_END_OF_SOURCE
)
{
nextAdGroupTimeMs
=
contentDurationMs
;
}
if
(
nextAdGroupTimeMs
-
contentPositionMs
<
MAXIMUM_PRELOAD_DURATION_MS
)
{
expectedAdGroupIndex
=
nextAdGroupIndex
;
}
}
}
else
{
}
else
{
return
VideoProgressUpdate
.
VIDEO_TIME_NOT_READY
;
return
VideoProgressUpdate
.
VIDEO_TIME_NOT_READY
;
}
}
...
@@ -807,15 +799,7 @@ public final class ImaAdsLoader
...
@@ -807,15 +799,7 @@ public final class ImaAdsLoader
@Override
@Override
public
VideoProgressUpdate
getAdProgress
()
{
public
VideoProgressUpdate
getAdProgress
()
{
if
(
player
==
null
)
{
throw
new
IllegalStateException
(
"Unexpected call to getAdProgress when using preloading"
);
return
lastAdProgress
;
}
else
if
(
imaAdState
!=
IMA_AD_STATE_NONE
&&
playingAd
)
{
long
adDuration
=
player
.
getDuration
();
return
adDuration
==
C
.
TIME_UNSET
?
VideoProgressUpdate
.
VIDEO_TIME_NOT_READY
:
new
VideoProgressUpdate
(
player
.
getCurrentPosition
(),
adDuration
);
}
else
{
return
VideoProgressUpdate
.
VIDEO_TIME_NOT_READY
;
}
}
}
@Override
@Override
...
@@ -841,30 +825,37 @@ public final class ImaAdsLoader
...
@@ -841,30 +825,37 @@ public final class ImaAdsLoader
}
}
@Override
@Override
public
void
loadAd
(
String
adUriString
)
{
public
void
loadAd
(
AdMediaInfo
adMediaInfo
,
AdPodInfo
adPodInfo
)
{
try
{
try
{
if
(
DEBUG
)
{
if
(
DEBUG
)
{
Log
.
d
(
TAG
,
"loadAd
in ad group "
+
adGroupIndex
);
Log
.
d
(
TAG
,
"loadAd
"
+
getAdMediaInfoString
(
adMediaInfo
)
+
", ad pod "
+
adPodInfo
);
}
}
if
(
adsManager
==
null
)
{
if
(
adsManager
==
null
)
{
// Drop events after release.
// Drop events after release.
return
;
return
;
}
}
if
(
adGroupIndex
==
C
.
INDEX_UNSET
)
{
int
adGroupIndex
=
getAdGroupIndex
(
adPodInfo
);
adGroupIndex
=
expectedAdGroupIndex
;
int
adIndexInAdGroup
=
adPodInfo
.
getAdPosition
()
-
1
;
adsManager
.
start
();
AdInfo
adInfo
=
new
AdInfo
(
adGroupIndex
,
adIndexInAdGroup
);
Log
.
w
(
adInfoByAdMediaInfo
.
put
(
adMediaInfo
,
adInfo
);
TAG
,
AdPlaybackState
.
AdGroup
adGroup
=
adPlaybackState
.
adGroups
[
adInfo
.
adGroupIndex
];
"Unexpected loadAd without LOADED event; assuming ad group index is actually "
if
(
adGroup
.
count
==
C
.
LENGTH_UNSET
)
{
+
expectedAdGroupIndex
);
adPlaybackState
=
adPlaybackState
.
withAdCount
(
adInfo
.
adGroupIndex
,
Math
.
max
(
adPodInfo
.
getTotalAds
(),
adGroup
.
states
.
length
));
adGroup
=
adPlaybackState
.
adGroups
[
adInfo
.
adGroupIndex
];
}
}
int
adIndexInAdGroup
=
getAdIndexInAdGroupToLoad
(
adGroupIndex
);
for
(
int
i
=
0
;
i
<
adIndexInAdGroup
;
i
++)
{
if
(
adIndexInAdGroup
==
C
.
INDEX_UNSET
)
{
// Any preceding ads that haven't loaded are not going to load.
Log
.
w
(
TAG
,
"Unexpected loadAd in an ad group with no remaining unavailable ads"
);
if
(
adGroup
.
states
[
i
]
==
AdPlaybackState
.
AD_STATE_UNAVAILABLE
)
{
return
;
adPlaybackState
=
adPlaybackState
.
withAdLoadError
(
/* adGroupIndex= */
adGroupIndex
,
/* adIndexInAdGroup= */
i
);
}
}
}
Uri
adUri
=
Uri
.
parse
(
adMediaInfo
.
getUrl
());
adPlaybackState
=
adPlaybackState
=
adPlaybackState
.
withAdUri
(
ad
GroupIndex
,
adIndexInAdGroup
,
Uri
.
parse
(
adUriString
)
);
adPlaybackState
.
withAdUri
(
ad
Info
.
adGroupIndex
,
adInfo
.
adIndexInAdGroup
,
adUri
);
updateAdPlaybackState
();
updateAdPlaybackState
();
}
catch
(
Exception
e
)
{
}
catch
(
Exception
e
)
{
maybeNotifyInternalError
(
"loadAd"
,
e
);
maybeNotifyInternalError
(
"loadAd"
,
e
);
...
@@ -882,69 +873,62 @@ public final class ImaAdsLoader
...
@@ -882,69 +873,62 @@ public final class ImaAdsLoader
}
}
@Override
@Override
public
void
playAd
()
{
public
void
playAd
(
AdMediaInfo
adMediaInfo
)
{
if
(
DEBUG
)
{
if
(
DEBUG
)
{
Log
.
d
(
TAG
,
"playAd
"
);
Log
.
d
(
TAG
,
"playAd
"
+
getAdMediaInfoString
(
adMediaInfo
)
);
}
}
if
(
adsManager
==
null
)
{
if
(
adsManager
==
null
)
{
// Drop events after release.
// Drop events after release.
return
;
return
;
}
}
switch
(
imaAdState
)
{
case
IMA_AD_STATE_PLAYING:
if
(
imaAdState
==
IMA_AD_STATE_PLAYING
)
{
// IMA does not always call stopAd before resuming content.
// IMA does not always call stopAd before resuming content.
// See [Internal: b/38354028, b/63320878].
// See [Internal: b/38354028].
Log
.
w
(
TAG
,
"Unexpected playAd without stopAd"
);
Log
.
w
(
TAG
,
"Unexpected playAd without stopAd"
);
break
;
}
case
IMA_AD_STATE_NONE:
// IMA is requesting to play the ad, so stop faking the content position.
if
(
imaAdState
==
IMA_AD_STATE_NONE
)
{
fakeContentProgressElapsedRealtimeMs
=
C
.
TIME_UNSET
;
// IMA is requesting to play the ad, so stop faking the content position.
fakeContentProgressOffsetMs
=
C
.
TIME_UNSET
;
fakeContentProgressElapsedRealtimeMs
=
C
.
TIME_UNSET
;
imaAdState
=
IMA_AD_STATE_PLAYING
;
fakeContentProgressOffsetMs
=
C
.
TIME_UNSET
;
for
(
int
i
=
0
;
i
<
adCallbacks
.
size
();
i
++)
{
imaAdState
=
IMA_AD_STATE_PLAYING
;
adCallbacks
.
get
(
i
).
onPlay
();
imaAdMediaInfo
=
adMediaInfo
;
}
imaAdInfo
=
Assertions
.
checkNotNull
(
adInfoByAdMediaInfo
.
get
(
adMediaInfo
));
if
(
shouldNotifyAdPrepareError
)
{
for
(
int
i
=
0
;
i
<
adCallbacks
.
size
();
i
++)
{
shouldNotifyAdPrepareError
=
false
;
adCallbacks
.
get
(
i
).
onPlay
(
adMediaInfo
);
for
(
int
i
=
0
;
i
<
adCallbacks
.
size
();
i
++)
{
}
adCallbacks
.
get
(
i
).
onError
();
if
(
pendingAdPrepareErrorAdInfo
!=
null
&&
pendingAdPrepareErrorAdInfo
.
equals
(
imaAdInfo
))
{
}
pendingAdPrepareErrorAdInfo
=
null
;
}
break
;
case
IMA_AD_STATE_PAUSED:
imaAdState
=
IMA_AD_STATE_PLAYING
;
for
(
int
i
=
0
;
i
<
adCallbacks
.
size
();
i
++)
{
for
(
int
i
=
0
;
i
<
adCallbacks
.
size
();
i
++)
{
adCallbacks
.
get
(
i
).
on
Resume
(
);
adCallbacks
.
get
(
i
).
on
Error
(
adMediaInfo
);
}
}
break
;
}
default
:
updateAdProgress
();
throw
new
IllegalStateException
();
}
else
{
imaAdState
=
IMA_AD_STATE_PLAYING
;
Assertions
.
checkState
(
adMediaInfo
.
equals
(
imaAdMediaInfo
));
for
(
int
i
=
0
;
i
<
adCallbacks
.
size
();
i
++)
{
adCallbacks
.
get
(
i
).
onResume
(
adMediaInfo
);
}
}
}
if
(
player
==
null
)
{
if
(!
Assertions
.
checkNotNull
(
player
).
getPlayWhenReady
())
{
// Sometimes messages from IMA arrive after detaching the player. See [Internal: b/63801642].
Log
.
w
(
TAG
,
"Unexpected playAd while detached"
);
}
else
if
(!
player
.
getPlayWhenReady
())
{
Assertions
.
checkNotNull
(
adsManager
).
pause
();
Assertions
.
checkNotNull
(
adsManager
).
pause
();
}
}
}
}
@Override
@Override
public
void
stopAd
()
{
public
void
stopAd
(
AdMediaInfo
adMediaInfo
)
{
if
(
DEBUG
)
{
if
(
DEBUG
)
{
Log
.
d
(
TAG
,
"stopAd
"
);
Log
.
d
(
TAG
,
"stopAd
"
+
getAdMediaInfoString
(
adMediaInfo
)
);
}
}
if
(
adsManager
==
null
)
{
if
(
adsManager
==
null
)
{
// Drop event after release.
// Drop event after release.
return
;
return
;
}
}
if
(
player
==
null
)
{
// Sometimes messages from IMA arrive after detaching the player. See [Internal: b/63801642].
Assertions
.
checkNotNull
(
player
);
Log
.
w
(
TAG
,
"Unexpected stopAd while detached"
);
Assertions
.
checkState
(
imaAdState
!=
IMA_AD_STATE_NONE
);
}
if
(
imaAdState
==
IMA_AD_STATE_NONE
)
{
Log
.
w
(
TAG
,
"Unexpected stopAd"
);
return
;
}
try
{
try
{
stopAdInternal
();
stopAdInternal
();
}
catch
(
Exception
e
)
{
}
catch
(
Exception
e
)
{
...
@@ -953,26 +937,21 @@ public final class ImaAdsLoader
...
@@ -953,26 +937,21 @@ public final class ImaAdsLoader
}
}
@Override
@Override
public
void
pauseAd
()
{
public
void
pauseAd
(
AdMediaInfo
adMediaInfo
)
{
if
(
DEBUG
)
{
if
(
DEBUG
)
{
Log
.
d
(
TAG
,
"pauseAd
"
);
Log
.
d
(
TAG
,
"pauseAd
"
+
getAdMediaInfoString
(
adMediaInfo
)
);
}
}
if
(
imaAdState
==
IMA_AD_STATE_NONE
)
{
if
(
imaAdState
==
IMA_AD_STATE_NONE
)
{
// This method is called after content is resumed.
// This method is called after content is resumed.
return
;
return
;
}
}
Assertions
.
checkState
(
adMediaInfo
.
equals
(
imaAdMediaInfo
));
imaAdState
=
IMA_AD_STATE_PAUSED
;
imaAdState
=
IMA_AD_STATE_PAUSED
;
for
(
int
i
=
0
;
i
<
adCallbacks
.
size
();
i
++)
{
for
(
int
i
=
0
;
i
<
adCallbacks
.
size
();
i
++)
{
adCallbacks
.
get
(
i
).
onPause
();
adCallbacks
.
get
(
i
).
onPause
(
adMediaInfo
);
}
}
}
}
@Override
public
void
resumeAd
()
{
// This method is never called. See [Internal: b/18931719].
maybeNotifyInternalError
(
"resumeAd"
,
new
IllegalStateException
(
"Unexpected call to resumeAd"
));
}
// Player.EventListener implementation.
// Player.EventListener implementation.
@Override
@Override
...
@@ -1021,8 +1000,9 @@ public final class ImaAdsLoader
...
@@ -1021,8 +1000,9 @@ public final class ImaAdsLoader
@Override
@Override
public
void
onPlayerError
(
ExoPlaybackException
error
)
{
public
void
onPlayerError
(
ExoPlaybackException
error
)
{
if
(
imaAdState
!=
IMA_AD_STATE_NONE
)
{
if
(
imaAdState
!=
IMA_AD_STATE_NONE
)
{
AdMediaInfo
adMediaInfo
=
Assertions
.
checkNotNull
(
imaAdMediaInfo
);
for
(
int
i
=
0
;
i
<
adCallbacks
.
size
();
i
++)
{
for
(
int
i
=
0
;
i
<
adCallbacks
.
size
();
i
++)
{
adCallbacks
.
get
(
i
).
onError
();
adCallbacks
.
get
(
i
).
onError
(
adMediaInfo
);
}
}
}
}
}
}
...
@@ -1064,25 +1044,13 @@ public final class ImaAdsLoader
...
@@ -1064,25 +1044,13 @@ public final class ImaAdsLoader
adsRenderingSettings
.
setPlayAdsAfterTime
(
midpointTimeUs
/
C
.
MICROS_PER_SECOND
);
adsRenderingSettings
.
setPlayAdsAfterTime
(
midpointTimeUs
/
C
.
MICROS_PER_SECOND
);
}
}
// IMA indexes any remaining midroll ad pods from 1. A preroll (if present) has index 0.
// Store an index offset as we want to index all ads (including skipped ones) from 0.
if
(
adGroupIndexForPosition
==
0
&&
adGroupTimesUs
[
0
]
==
0
)
{
// We are playing a preroll.
podIndexOffset
=
0
;
}
else
if
(
adGroupIndexForPosition
==
C
.
INDEX_UNSET
)
{
// There's no ad to play which means there's no preroll.
podIndexOffset
=
-
1
;
}
else
{
// We are playing a midroll and any ads before it were skipped.
podIndexOffset
=
adGroupIndexForPosition
-
1
;
}
if
(
adGroupIndexForPosition
!=
C
.
INDEX_UNSET
&&
hasMidrollAdGroups
(
adGroupTimesUs
))
{
if
(
adGroupIndexForPosition
!=
C
.
INDEX_UNSET
&&
hasMidrollAdGroups
(
adGroupTimesUs
))
{
// Provide the player's initial position to trigger loading and playing the ad.
// Provide the player's initial position to trigger loading and playing the ad.
pendingContentPositionMs
=
contentPositionMs
;
pendingContentPositionMs
=
contentPositionMs
;
}
}
adsManager
.
init
(
adsRenderingSettings
);
adsManager
.
init
(
adsRenderingSettings
);
adsManager
.
start
();
updateAdPlaybackState
();
updateAdPlaybackState
();
if
(
DEBUG
)
{
if
(
DEBUG
)
{
Log
.
d
(
TAG
,
"Initialized with ads rendering settings: "
+
adsRenderingSettings
);
Log
.
d
(
TAG
,
"Initialized with ads rendering settings: "
+
adsRenderingSettings
);
...
@@ -1090,39 +1058,32 @@ public final class ImaAdsLoader
...
@@ -1090,39 +1058,32 @@ public final class ImaAdsLoader
}
}
private
void
handleAdEvent
(
AdEvent
adEvent
)
{
private
void
handleAdEvent
(
AdEvent
adEvent
)
{
Ad
ad
=
adEvent
.
getAd
();
switch
(
adEvent
.
getType
())
{
switch
(
adEvent
.
getType
())
{
case
LOADED:
case
AD_BREAK_FETCH_ERROR:
// The ad position is not always accurate when using preloading. See [Internal: b/62613240].
String
adGroupTimeSecondsString
=
AdPodInfo
adPodInfo
=
ad
.
getAdPodInfo
();
Assertions
.
checkNotNull
(
adEvent
.
getAdData
().
get
(
"adBreakTime"
));
int
podIndex
=
adPodInfo
.
getPodIndex
();
adGroupIndex
=
podIndex
==
-
1
?
(
adPlaybackState
.
adGroupCount
-
1
)
:
(
podIndex
+
podIndexOffset
);
int
adPosition
=
adPodInfo
.
getAdPosition
();
int
adCount
=
adPodInfo
.
getTotalAds
();
Assertions
.
checkNotNull
(
adsManager
).
start
();
if
(
DEBUG
)
{
if
(
DEBUG
)
{
Log
.
d
(
TAG
,
"
Loaded ad "
+
adPosition
+
" of "
+
adCount
+
" in group "
+
adGroupIndex
);
Log
.
d
(
TAG
,
"
Fetch error for ad at "
+
adGroupTimeSecondsString
+
" seconds"
);
}
}
int
oldAdCount
=
adPlaybackState
.
adGroups
[
adGroupIndex
].
count
;
int
adGroupTimeSeconds
=
Integer
.
parseInt
(
adGroupTimeSecondsString
)
;
i
f
(
adCount
!=
oldAdCount
)
{
i
nt
adGroupIndex
=
if
(
oldAdCount
==
C
.
LENGTH_UNSET
)
{
Arrays
.
binarySearch
(
adPlaybackState
=
adPlaybackState
.
withAdCount
(
adGroupIndex
,
adCount
);
adPlaybackState
.
adGroupTimesUs
,
C
.
MICROS_PER_SECOND
*
adGroupTimeSeconds
);
updateAdPlaybackState
()
;
AdPlaybackState
.
AdGroup
adGroup
=
adPlaybackState
.
adGroups
[
adGroupIndex
]
;
}
else
{
if
(
adGroup
.
count
==
C
.
LENGTH_UNSET
)
{
// IMA sometimes unexpectedly decreases the ad count in an ad group.
adPlaybackState
=
Log
.
w
(
TAG
,
"Unexpected ad count in LOADED, "
+
adCount
+
", expected "
+
oldAdCount
);
adPlaybackState
.
withAdCount
(
adGroupIndex
,
Math
.
max
(
1
,
adGroup
.
states
.
length
)
);
}
adGroup
=
adPlaybackState
.
adGroups
[
adGroupIndex
];
}
}
if
(
adGroupIndex
!=
expectedAdGroupIndex
)
{
for
(
int
i
=
0
;
i
<
adGroup
.
count
;
i
++)
{
Log
.
w
(
if
(
adGroup
.
states
[
i
]
==
AdPlaybackState
.
AD_STATE_UNAVAILABLE
)
{
TAG
,
if
(
DEBUG
)
{
"Expected ad group index "
Log
.
d
(
TAG
,
"Removing ad "
+
i
+
" in ad group "
+
adGroupIndex
);
+
expectedAdGroupIndex
}
+
", actual ad group index "
adPlaybackState
=
adPlaybackState
.
withAdLoadError
(
adGroupIndex
,
i
);
+
adGroupIndex
);
}
expectedAdGroupIndex
=
adGroupIndex
;
}
}
updateAdPlaybackState
();
break
;
break
;
case
CONTENT_PAUSE_REQUESTED:
case
CONTENT_PAUSE_REQUESTED:
// After CONTENT_PAUSE_REQUESTED, IMA will playAd/pauseAd/stopAd to show one or more ads
// After CONTENT_PAUSE_REQUESTED, IMA will playAd/pauseAd/stopAd to show one or more ads
...
@@ -1148,23 +1109,65 @@ public final class ImaAdsLoader
...
@@ -1148,23 +1109,65 @@ public final class ImaAdsLoader
Map
<
String
,
String
>
adData
=
adEvent
.
getAdData
();
Map
<
String
,
String
>
adData
=
adEvent
.
getAdData
();
String
message
=
"AdEvent: "
+
adData
;
String
message
=
"AdEvent: "
+
adData
;
Log
.
i
(
TAG
,
message
);
Log
.
i
(
TAG
,
message
);
if
(
"adLoadError"
.
equals
(
adData
.
get
(
"type"
)))
{
handleAdGroupLoadError
(
new
IOException
(
message
));
}
break
;
break
;
default
:
default
:
break
;
break
;
}
}
}
}
private
VideoProgressUpdate
getAdVideoProgressUpdate
()
{
if
(
player
==
null
)
{
return
lastAdProgress
;
}
else
if
(
imaAdState
!=
IMA_AD_STATE_NONE
&&
playingAd
)
{
long
adDuration
=
player
.
getDuration
();
return
adDuration
==
C
.
TIME_UNSET
?
VideoProgressUpdate
.
VIDEO_TIME_NOT_READY
:
new
VideoProgressUpdate
(
player
.
getCurrentPosition
(),
adDuration
);
}
else
{
return
VideoProgressUpdate
.
VIDEO_TIME_NOT_READY
;
}
}
private
void
updateAdProgress
()
{
VideoProgressUpdate
videoProgressUpdate
=
getAdVideoProgressUpdate
();
AdMediaInfo
adMediaInfo
=
Assertions
.
checkNotNull
(
imaAdMediaInfo
);
for
(
int
i
=
0
;
i
<
adCallbacks
.
size
();
i
++)
{
adCallbacks
.
get
(
i
).
onAdProgress
(
adMediaInfo
,
videoProgressUpdate
);
}
handler
.
removeCallbacks
(
updateAdProgressRunnable
);
handler
.
postDelayed
(
updateAdProgressRunnable
,
AD_PROGRESS_UPDATE_INTERVAL_MS
);
}
private
void
stopUpdatingAdProgress
()
{
handler
.
removeCallbacks
(
updateAdProgressRunnable
);
}
private
void
handlePlayerStateChanged
(
boolean
playWhenReady
,
@Player
.
State
int
playbackState
)
{
private
void
handlePlayerStateChanged
(
boolean
playWhenReady
,
@Player
.
State
int
playbackState
)
{
if
(
playingAd
&&
imaAdState
==
IMA_AD_STATE_PLAYING
)
{
if
(!
bufferingAd
&&
playbackState
==
Player
.
STATE_BUFFERING
)
{
AdMediaInfo
adMediaInfo
=
Assertions
.
checkNotNull
(
imaAdMediaInfo
);
for
(
int
i
=
0
;
i
<
adCallbacks
.
size
();
i
++)
{
adCallbacks
.
get
(
i
).
onBuffering
(
adMediaInfo
);
}
stopUpdatingAdProgress
();
}
else
if
(
bufferingAd
&&
playbackState
==
Player
.
STATE_READY
)
{
bufferingAd
=
false
;
updateAdProgress
();
}
}
if
(
imaAdState
==
IMA_AD_STATE_NONE
if
(
imaAdState
==
IMA_AD_STATE_NONE
&&
playbackState
==
Player
.
STATE_BUFFERING
&&
playbackState
==
Player
.
STATE_BUFFERING
&&
playWhenReady
)
{
&&
playWhenReady
)
{
checkForContentComplete
();
checkForContentComplete
();
}
else
if
(
imaAdState
!=
IMA_AD_STATE_NONE
&&
playbackState
==
Player
.
STATE_ENDED
)
{
}
else
if
(
imaAdState
!=
IMA_AD_STATE_NONE
&&
playbackState
==
Player
.
STATE_ENDED
)
{
for
(
int
i
=
0
;
i
<
adCallbacks
.
size
();
i
++)
{
AdMediaInfo
adMediaInfo
=
Assertions
.
checkNotNull
(
imaAdMediaInfo
);
adCallbacks
.
get
(
i
).
onEnded
();
if
(
adMediaInfo
==
null
)
{
Log
.
w
(
TAG
,
"onEnded without ad media info"
);
}
else
{
for
(
int
i
=
0
;
i
<
adCallbacks
.
size
();
i
++)
{
adCallbacks
.
get
(
i
).
onEnded
(
adMediaInfo
);
}
}
}
if
(
DEBUG
)
{
if
(
DEBUG
)
{
Log
.
d
(
TAG
,
"VideoAdPlayerCallback.onEnded in onPlayerStateChanged"
);
Log
.
d
(
TAG
,
"VideoAdPlayerCallback.onEnded in onPlayerStateChanged"
);
...
@@ -1193,9 +1196,6 @@ public final class ImaAdsLoader
...
@@ -1193,9 +1196,6 @@ public final class ImaAdsLoader
if
(
newAdGroupIndex
!=
C
.
INDEX_UNSET
)
{
if
(
newAdGroupIndex
!=
C
.
INDEX_UNSET
)
{
sentPendingContentPositionMs
=
false
;
sentPendingContentPositionMs
=
false
;
pendingContentPositionMs
=
positionMs
;
pendingContentPositionMs
=
positionMs
;
if
(
newAdGroupIndex
!=
adGroupIndex
)
{
shouldNotifyAdPrepareError
=
false
;
}
}
}
}
}
}
}
...
@@ -1208,8 +1208,13 @@ public final class ImaAdsLoader
...
@@ -1208,8 +1208,13 @@ public final class ImaAdsLoader
if
(
adFinished
)
{
if
(
adFinished
)
{
// IMA is waiting for the ad playback to finish so invoke the callback now.
// IMA is waiting for the ad playback to finish so invoke the callback now.
// Either CONTENT_RESUME_REQUESTED will be passed next, or playAd will be called again.
// Either CONTENT_RESUME_REQUESTED will be passed next, or playAd will be called again.
for
(
int
i
=
0
;
i
<
adCallbacks
.
size
();
i
++)
{
@Nullable
AdMediaInfo
adMediaInfo
=
imaAdMediaInfo
;
adCallbacks
.
get
(
i
).
onEnded
();
if
(
adMediaInfo
==
null
)
{
Log
.
w
(
TAG
,
"onEnded without ad media info"
);
}
else
{
for
(
int
i
=
0
;
i
<
adCallbacks
.
size
();
i
++)
{
adCallbacks
.
get
(
i
).
onEnded
(
adMediaInfo
);
}
}
}
if
(
DEBUG
)
{
if
(
DEBUG
)
{
Log
.
d
(
TAG
,
"VideoAdPlayerCallback.onEnded in onTimelineChanged/onPositionDiscontinuity"
);
Log
.
d
(
TAG
,
"VideoAdPlayerCallback.onEnded in onTimelineChanged/onPositionDiscontinuity"
);
...
@@ -1227,15 +1232,8 @@ public final class ImaAdsLoader
...
@@ -1227,15 +1232,8 @@ public final class ImaAdsLoader
}
}
private
void
resumeContentInternal
()
{
private
void
resumeContentInternal
()
{
if
(
imaAdState
!=
IMA_AD_STATE_NONE
)
{
if
(
imaAdInfo
!=
null
)
{
imaAdState
=
IMA_AD_STATE_NONE
;
adPlaybackState
=
adPlaybackState
.
withSkippedAdGroup
(
imaAdInfo
.
adGroupIndex
);
if
(
DEBUG
)
{
Log
.
d
(
TAG
,
"Unexpected CONTENT_RESUME_REQUESTED without stopAd"
);
}
}
if
(
adGroupIndex
!=
C
.
INDEX_UNSET
)
{
adPlaybackState
=
adPlaybackState
.
withSkippedAdGroup
(
adGroupIndex
);
adGroupIndex
=
C
.
INDEX_UNSET
;
updateAdPlaybackState
();
updateAdPlaybackState
();
}
}
}
}
...
@@ -1250,23 +1248,40 @@ public final class ImaAdsLoader
...
@@ -1250,23 +1248,40 @@ public final class ImaAdsLoader
private
void
stopAdInternal
()
{
private
void
stopAdInternal
()
{
imaAdState
=
IMA_AD_STATE_NONE
;
imaAdState
=
IMA_AD_STATE_NONE
;
int
adIndexInAdGroup
=
adPlaybackState
.
adGroups
[
adGroupIndex
].
getFirstAdIndexToPlay
();
stopUpdatingAdProgress
();
// TODO: Handle the skipped event so the ad can be marked as skipped rather than played.
// TODO: Handle the skipped event so the ad can be marked as skipped rather than played.
Assertions
.
checkNotNull
(
imaAdInfo
);
int
adGroupIndex
=
imaAdInfo
.
adGroupIndex
;
int
adIndexInAdGroup
=
imaAdInfo
.
adIndexInAdGroup
;
adPlaybackState
=
adPlaybackState
=
adPlaybackState
.
withPlayedAd
(
adGroupIndex
,
adIndexInAdGroup
).
withAdResumePositionUs
(
0
);
adPlaybackState
.
withPlayedAd
(
adGroupIndex
,
adIndexInAdGroup
).
withAdResumePositionUs
(
0
);
updateAdPlaybackState
();
updateAdPlaybackState
();
if
(!
playingAd
)
{
if
(!
playingAd
)
{
adGroupIndex
=
C
.
INDEX_UNSET
;
imaAdMediaInfo
=
null
;
imaAdInfo
=
null
;
}
}
}
}
private
void
handleAdGroupLoadError
(
Exception
error
)
{
private
void
handleAdGroupLoadError
(
Exception
error
)
{
if
(
player
==
null
)
{
return
;
}
// TODO: Once IMA signals which ad group failed to load, clean up this code.
long
playerPositionMs
=
player
.
getContentPosition
();
int
adGroupIndex
=
int
adGroupIndex
=
this
.
adGroupIndex
==
C
.
INDEX_UNSET
?
expectedAdGroupIndex
:
this
.
adGroupIndex
;
adPlaybackState
.
getAdGroupIndexForPositionUs
(
C
.
msToUs
(
playerPositionMs
),
C
.
msToUs
(
contentDurationMs
));
if
(
adGroupIndex
==
C
.
INDEX_UNSET
)
{
if
(
adGroupIndex
==
C
.
INDEX_UNSET
)
{
// Drop the error, as we don't know which ad group it relates to.
adGroupIndex
=
return
;
adPlaybackState
.
getAdGroupIndexAfterPositionUs
(
C
.
msToUs
(
playerPositionMs
),
C
.
msToUs
(
contentDurationMs
));
if
(
adGroupIndex
==
C
.
INDEX_UNSET
)
{
// The error doesn't seem to relate to any ad group so give up handling it.
return
;
}
}
}
AdPlaybackState
.
AdGroup
adGroup
=
adPlaybackState
.
adGroups
[
adGroupIndex
];
AdPlaybackState
.
AdGroup
adGroup
=
adPlaybackState
.
adGroups
[
adGroupIndex
];
if
(
adGroup
.
count
==
C
.
LENGTH_UNSET
)
{
if
(
adGroup
.
count
==
C
.
LENGTH_UNSET
)
{
adPlaybackState
=
adPlaybackState
=
...
@@ -1306,19 +1321,20 @@ public final class ImaAdsLoader
...
@@ -1306,19 +1321,20 @@ public final class ImaAdsLoader
if
(
fakeContentProgressOffsetMs
==
C
.
TIME_END_OF_SOURCE
)
{
if
(
fakeContentProgressOffsetMs
==
C
.
TIME_END_OF_SOURCE
)
{
fakeContentProgressOffsetMs
=
contentDurationMs
;
fakeContentProgressOffsetMs
=
contentDurationMs
;
}
}
shouldNotifyAdPrepareError
=
true
;
pendingAdPrepareErrorAdInfo
=
new
AdInfo
(
adGroupIndex
,
adIndexInAdGroup
)
;
}
else
{
}
else
{
AdMediaInfo
adMediaInfo
=
Assertions
.
checkNotNull
(
imaAdMediaInfo
);
// We're already playing an ad.
// We're already playing an ad.
if
(
adIndexInAdGroup
>
playingAdIndexInAdGroup
)
{
if
(
adIndexInAdGroup
>
playingAdIndexInAdGroup
)
{
// Mark the playing ad as ended so we can notify the error on the next ad and remove it,
// Mark the playing ad as ended so we can notify the error on the next ad and remove it,
// which means that the ad after will load (if any).
// which means that the ad after will load (if any).
for
(
int
i
=
0
;
i
<
adCallbacks
.
size
();
i
++)
{
for
(
int
i
=
0
;
i
<
adCallbacks
.
size
();
i
++)
{
adCallbacks
.
get
(
i
).
onEnded
();
adCallbacks
.
get
(
i
).
onEnded
(
adMediaInfo
);
}
}
}
}
playingAdIndexInAdGroup
=
adPlaybackState
.
adGroups
[
adGroupIndex
].
getFirstAdIndexToPlay
();
playingAdIndexInAdGroup
=
adPlaybackState
.
adGroups
[
adGroupIndex
].
getFirstAdIndexToPlay
();
for
(
int
i
=
0
;
i
<
adCallbacks
.
size
();
i
++)
{
for
(
int
i
=
0
;
i
<
adCallbacks
.
size
();
i
++)
{
adCallbacks
.
get
(
i
).
onError
();
adCallbacks
.
get
(
i
).
onError
(
Assertions
.
checkNotNull
(
adMediaInfo
)
);
}
}
}
}
adPlaybackState
=
adPlaybackState
.
withAdLoadError
(
adGroupIndex
,
adIndexInAdGroup
);
adPlaybackState
=
adPlaybackState
.
withAdLoadError
(
adGroupIndex
,
adIndexInAdGroup
);
...
@@ -1336,11 +1352,6 @@ public final class ImaAdsLoader
...
@@ -1336,11 +1352,6 @@ public final class ImaAdsLoader
Log
.
d
(
TAG
,
"adsLoader.contentComplete"
);
Log
.
d
(
TAG
,
"adsLoader.contentComplete"
);
}
}
sentContentComplete
=
true
;
sentContentComplete
=
true
;
// After sending content complete IMA will not poll the content position, so set the expected
// ad group index.
expectedAdGroupIndex
=
adPlaybackState
.
getAdGroupIndexForPositionUs
(
C
.
msToUs
(
contentDurationMs
),
C
.
msToUs
(
contentDurationMs
));
}
}
}
}
...
@@ -1351,21 +1362,6 @@ public final class ImaAdsLoader
...
@@ -1351,21 +1362,6 @@ public final class ImaAdsLoader
}
}
}
}
/**
* Returns the next ad index in the specified ad group to load, or {@link C#INDEX_UNSET} if all
* ads in the ad group have loaded.
*/
private
int
getAdIndexInAdGroupToLoad
(
int
adGroupIndex
)
{
@AdState
int
[]
states
=
adPlaybackState
.
adGroups
[
adGroupIndex
].
states
;
int
adIndexInAdGroup
=
0
;
// IMA loads ads in order.
while
(
adIndexInAdGroup
<
states
.
length
&&
states
[
adIndexInAdGroup
]
!=
AdPlaybackState
.
AD_STATE_UNAVAILABLE
)
{
adIndexInAdGroup
++;
}
return
adIndexInAdGroup
==
states
.
length
?
C
.
INDEX_UNSET
:
adIndexInAdGroup
;
}
private
void
maybeNotifyPendingAdLoadError
()
{
private
void
maybeNotifyPendingAdLoadError
()
{
if
(
pendingAdLoadError
!=
null
&&
eventListener
!=
null
)
{
if
(
pendingAdLoadError
!=
null
&&
eventListener
!=
null
)
{
eventListener
.
onAdLoadError
(
pendingAdLoadError
,
getAdsDataSpec
(
adTagUri
));
eventListener
.
onAdLoadError
(
pendingAdLoadError
,
getAdsDataSpec
(
adTagUri
));
...
@@ -1399,6 +1395,22 @@ public final class ImaAdsLoader
...
@@ -1399,6 +1395,22 @@ public final class ImaAdsLoader
-
timeline
.
getPeriod
(
/* periodIndex= */
0
,
period
).
getPositionInWindowMs
();
-
timeline
.
getPeriod
(
/* periodIndex= */
0
,
period
).
getPositionInWindowMs
();
}
}
private
int
getAdGroupIndex
(
AdPodInfo
adPodInfo
)
{
if
(
adPodInfo
.
getPodIndex
()
==
-
1
)
{
// This is a postroll ad.
return
adPlaybackState
.
adGroupCount
-
1
;
}
// adPodInfo.podIndex may be 0-based or 1-based, so for now look up the cue point instead.
long
adGroupTimeUs
=
(
long
)
(((
float
)
adPodInfo
.
getTimeOffset
())
*
C
.
MICROS_PER_SECOND
);
for
(
int
adGroupIndex
=
0
;
adGroupIndex
<
adPlaybackState
.
adGroupCount
;
adGroupIndex
++)
{
if
(
adPlaybackState
.
adGroupTimesUs
[
adGroupIndex
]
==
adGroupTimeUs
)
{
return
adGroupIndex
;
}
}
throw
new
IllegalStateException
(
"Failed to find cue point"
);
}
private
static
long
[]
getAdGroupTimesUs
(
List
<
Float
>
cuePoints
)
{
private
static
long
[]
getAdGroupTimesUs
(
List
<
Float
>
cuePoints
)
{
if
(
cuePoints
.
isEmpty
())
{
if
(
cuePoints
.
isEmpty
())
{
// If no cue points are specified, there is a preroll ad.
// If no cue points are specified, there is a preroll ad.
...
@@ -1428,6 +1440,12 @@ public final class ImaAdsLoader
...
@@ -1428,6 +1440,12 @@ public final class ImaAdsLoader
||
adError
.
getErrorCode
()
==
AdErrorCode
.
UNKNOWN_ERROR
;
||
adError
.
getErrorCode
()
==
AdErrorCode
.
UNKNOWN_ERROR
;
}
}
private
static
Looper
getImaLooper
()
{
// IMA SDK callbacks occur on the main thread. This method can be used to check that the player
// is using the same looper, to ensure all interaction with this class is on the main thread.
return
Looper
.
getMainLooper
();
}
private
static
boolean
hasMidrollAdGroups
(
long
[]
adGroupTimesUs
)
{
private
static
boolean
hasMidrollAdGroups
(
long
[]
adGroupTimesUs
)
{
int
count
=
adGroupTimesUs
.
length
;
int
count
=
adGroupTimesUs
.
length
;
if
(
count
==
1
)
{
if
(
count
==
1
)
{
...
@@ -1456,6 +1474,49 @@ public final class ImaAdsLoader
...
@@ -1456,6 +1474,49 @@ public final class ImaAdsLoader
Context
context
,
ImaSdkSettings
imaSdkSettings
,
AdDisplayContainer
adDisplayContainer
);
Context
context
,
ImaSdkSettings
imaSdkSettings
,
AdDisplayContainer
adDisplayContainer
);
}
}
private
String
getAdMediaInfoString
(
AdMediaInfo
adMediaInfo
)
{
@Nullable
AdInfo
adInfo
=
adInfoByAdMediaInfo
.
get
(
adMediaInfo
);
return
"AdMediaInfo["
+
adMediaInfo
.
getUrl
()
+
(
adInfo
!=
null
?
", "
+
adInfo
:
""
)
+
"]"
;
}
// TODO: Consider moving this into AdPlaybackState.
private
static
final
class
AdInfo
{
public
final
int
adGroupIndex
;
public
final
int
adIndexInAdGroup
;
public
AdInfo
(
int
adGroupIndex
,
int
adIndexInAdGroup
)
{
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
;
}
AdInfo
adInfo
=
(
AdInfo
)
o
;
if
(
adGroupIndex
!=
adInfo
.
adGroupIndex
)
{
return
false
;
}
return
adIndexInAdGroup
==
adInfo
.
adIndexInAdGroup
;
}
@Override
public
int
hashCode
()
{
int
result
=
adGroupIndex
;
result
=
31
*
result
+
adIndexInAdGroup
;
return
result
;
}
@Override
public
String
toString
()
{
return
"("
+
adGroupIndex
+
", "
+
adIndexInAdGroup
+
')'
;
}
}
/** Default {@link ImaFactory} for non-test usage, which delegates to {@link ImaSdkFactory}. */
/** Default {@link ImaFactory} for non-test usage, which delegates to {@link ImaSdkFactory}. */
private
static
final
class
DefaultImaFactory
implements
ImaFactory
{
private
static
final
class
DefaultImaFactory
implements
ImaFactory
{
@Override
@Override
...
...
extensions/ima/src/test/java/com/google/android/exoplayer2/ext/ima/ImaAdsLoaderTest.java
View file @
3b99a84d
...
@@ -41,6 +41,7 @@ import com.google.ads.interactivemedia.v3.api.AdsManagerLoadedEvent;
...
@@ -41,6 +41,7 @@ import com.google.ads.interactivemedia.v3.api.AdsManagerLoadedEvent;
import
com.google.ads.interactivemedia.v3.api.AdsRenderingSettings
;
import
com.google.ads.interactivemedia.v3.api.AdsRenderingSettings
;
import
com.google.ads.interactivemedia.v3.api.AdsRequest
;
import
com.google.ads.interactivemedia.v3.api.AdsRequest
;
import
com.google.ads.interactivemedia.v3.api.ImaSdkSettings
;
import
com.google.ads.interactivemedia.v3.api.ImaSdkSettings
;
import
com.google.ads.interactivemedia.v3.api.player.AdMediaInfo
;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.ExoPlaybackException
;
import
com.google.android.exoplayer2.ExoPlaybackException
;
import
com.google.android.exoplayer2.Player
;
import
com.google.android.exoplayer2.Player
;
...
@@ -85,6 +86,7 @@ public final class ImaAdsLoaderTest {
...
@@ -85,6 +86,7 @@ public final class ImaAdsLoaderTest {
private
static
final
long
CONTENT_PERIOD_DURATION_US
=
private
static
final
long
CONTENT_PERIOD_DURATION_US
=
CONTENT_TIMELINE
.
getPeriod
(
/* periodIndex= */
0
,
new
Period
()).
durationUs
;
CONTENT_TIMELINE
.
getPeriod
(
/* periodIndex= */
0
,
new
Period
()).
durationUs
;
private
static
final
Uri
TEST_URI
=
Uri
.
EMPTY
;
private
static
final
Uri
TEST_URI
=
Uri
.
EMPTY
;
private
static
final
AdMediaInfo
TEST_AD_MEDIA_INFO
=
new
AdMediaInfo
(
TEST_URI
.
toString
());
private
static
final
long
TEST_AD_DURATION_US
=
5
*
C
.
MICROS_PER_SECOND
;
private
static
final
long
TEST_AD_DURATION_US
=
5
*
C
.
MICROS_PER_SECOND
;
private
static
final
long
[][]
PREROLL_ADS_DURATIONS_US
=
new
long
[][]
{{
TEST_AD_DURATION_US
}};
private
static
final
long
[][]
PREROLL_ADS_DURATIONS_US
=
new
long
[][]
{{
TEST_AD_DURATION_US
}};
private
static
final
Float
[]
PREROLL_CUE_POINTS_SECONDS
=
new
Float
[]
{
0
f
};
private
static
final
Float
[]
PREROLL_CUE_POINTS_SECONDS
=
new
Float
[]
{
0
f
};
...
@@ -99,7 +101,7 @@ public final class ImaAdsLoaderTest {
...
@@ -99,7 +101,7 @@ public final class ImaAdsLoaderTest {
@Mock
private
AdsManagerLoadedEvent
mockAdsManagerLoadedEvent
;
@Mock
private
AdsManagerLoadedEvent
mockAdsManagerLoadedEvent
;
@Mock
private
com
.
google
.
ads
.
interactivemedia
.
v3
.
api
.
AdsLoader
mockAdsLoader
;
@Mock
private
com
.
google
.
ads
.
interactivemedia
.
v3
.
api
.
AdsLoader
mockAdsLoader
;
@Mock
private
ImaFactory
mockImaFactory
;
@Mock
private
ImaFactory
mockImaFactory
;
@Mock
private
AdPodInfo
mock
PrerollSingleAd
AdPodInfo
;
@Mock
private
AdPodInfo
mockAdPodInfo
;
@Mock
private
Ad
mockPrerollSingleAd
;
@Mock
private
Ad
mockPrerollSingleAd
;
private
ViewGroup
adViewGroup
;
private
ViewGroup
adViewGroup
;
...
@@ -195,12 +197,12 @@ public final class ImaAdsLoaderTest {
...
@@ -195,12 +197,12 @@ public final class ImaAdsLoaderTest {
// SDK being proguarded.
// SDK being proguarded.
imaAdsLoader
.
requestAds
(
adViewGroup
);
imaAdsLoader
.
requestAds
(
adViewGroup
);
imaAdsLoader
.
onAdEvent
(
getAdEvent
(
AdEventType
.
LOADED
,
mockPrerollSingleAd
));
imaAdsLoader
.
onAdEvent
(
getAdEvent
(
AdEventType
.
LOADED
,
mockPrerollSingleAd
));
imaAdsLoader
.
loadAd
(
TEST_
URI
.
toString
()
);
imaAdsLoader
.
loadAd
(
TEST_
AD_MEDIA_INFO
,
mockAdPodInfo
);
imaAdsLoader
.
onAdEvent
(
getAdEvent
(
AdEventType
.
CONTENT_PAUSE_REQUESTED
,
mockPrerollSingleAd
));
imaAdsLoader
.
onAdEvent
(
getAdEvent
(
AdEventType
.
CONTENT_PAUSE_REQUESTED
,
mockPrerollSingleAd
));
imaAdsLoader
.
playAd
();
imaAdsLoader
.
playAd
(
TEST_AD_MEDIA_INFO
);
imaAdsLoader
.
onAdEvent
(
getAdEvent
(
AdEventType
.
STARTED
,
mockPrerollSingleAd
));
imaAdsLoader
.
onAdEvent
(
getAdEvent
(
AdEventType
.
STARTED
,
mockPrerollSingleAd
));
imaAdsLoader
.
pauseAd
();
imaAdsLoader
.
pauseAd
(
TEST_AD_MEDIA_INFO
);
imaAdsLoader
.
stopAd
();
imaAdsLoader
.
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
(
Player
.
DISCONTINUITY_REASON_SEEK
);
imaAdsLoader
.
onAdEvent
(
getAdEvent
(
AdEventType
.
CONTENT_RESUME_REQUESTED
,
/* ad= */
null
));
imaAdsLoader
.
onAdEvent
(
getAdEvent
(
AdEventType
.
CONTENT_RESUME_REQUESTED
,
/* ad= */
null
));
...
@@ -215,11 +217,11 @@ public final class ImaAdsLoaderTest {
...
@@ -215,11 +217,11 @@ public final class ImaAdsLoaderTest {
// Load the preroll ad.
// Load the preroll ad.
imaAdsLoader
.
start
(
adsLoaderListener
,
adViewProvider
);
imaAdsLoader
.
start
(
adsLoaderListener
,
adViewProvider
);
imaAdsLoader
.
onAdEvent
(
getAdEvent
(
AdEventType
.
LOADED
,
mockPrerollSingleAd
));
imaAdsLoader
.
onAdEvent
(
getAdEvent
(
AdEventType
.
LOADED
,
mockPrerollSingleAd
));
imaAdsLoader
.
loadAd
(
TEST_
URI
.
toString
()
);
imaAdsLoader
.
loadAd
(
TEST_
AD_MEDIA_INFO
,
mockAdPodInfo
);
imaAdsLoader
.
onAdEvent
(
getAdEvent
(
AdEventType
.
CONTENT_PAUSE_REQUESTED
,
mockPrerollSingleAd
));
imaAdsLoader
.
onAdEvent
(
getAdEvent
(
AdEventType
.
CONTENT_PAUSE_REQUESTED
,
mockPrerollSingleAd
));
// Play the preroll ad.
// Play the preroll ad.
imaAdsLoader
.
playAd
();
imaAdsLoader
.
playAd
(
TEST_AD_MEDIA_INFO
);
fakeExoPlayer
.
setPlayingAdPosition
(
fakeExoPlayer
.
setPlayingAdPosition
(
/* adGroupIndex= */
0
,
/* adGroupIndex= */
0
,
/* adIndexInAdGroup= */
0
,
/* adIndexInAdGroup= */
0
,
...
@@ -233,7 +235,7 @@ public final class ImaAdsLoaderTest {
...
@@ -233,7 +235,7 @@ public final class ImaAdsLoaderTest {
// Play the content.
// Play the content.
fakeExoPlayer
.
setPlayingContentPosition
(
0
);
fakeExoPlayer
.
setPlayingContentPosition
(
0
);
imaAdsLoader
.
stopAd
();
imaAdsLoader
.
stopAd
(
TEST_AD_MEDIA_INFO
);
imaAdsLoader
.
onAdEvent
(
getAdEvent
(
AdEventType
.
CONTENT_RESUME_REQUESTED
,
/* ad= */
null
));
imaAdsLoader
.
onAdEvent
(
getAdEvent
(
AdEventType
.
CONTENT_RESUME_REQUESTED
,
/* ad= */
null
));
// Verify that the preroll ad has been marked as played.
// Verify that the preroll ad has been marked as played.
...
@@ -313,11 +315,11 @@ public final class ImaAdsLoaderTest {
...
@@ -313,11 +315,11 @@ public final class ImaAdsLoaderTest {
when
(
mockImaFactory
.
createAdsRequest
()).
thenReturn
(
mockAdsRequest
);
when
(
mockImaFactory
.
createAdsRequest
()).
thenReturn
(
mockAdsRequest
);
when
(
mockImaFactory
.
createAdsLoader
(
any
(),
any
(),
any
())).
thenReturn
(
mockAdsLoader
);
when
(
mockImaFactory
.
createAdsLoader
(
any
(),
any
(),
any
())).
thenReturn
(
mockAdsLoader
);
when
(
mock
PrerollSingleAd
AdPodInfo
.
getPodIndex
()).
thenReturn
(
0
);
when
(
mockAdPodInfo
.
getPodIndex
()).
thenReturn
(
0
);
when
(
mock
PrerollSingleAd
AdPodInfo
.
getTotalAds
()).
thenReturn
(
1
);
when
(
mockAdPodInfo
.
getTotalAds
()).
thenReturn
(
1
);
when
(
mock
PrerollSingleAd
AdPodInfo
.
getAdPosition
()).
thenReturn
(
1
);
when
(
mockAdPodInfo
.
getAdPosition
()).
thenReturn
(
1
);
when
(
mockPrerollSingleAd
.
getAdPodInfo
()).
thenReturn
(
mock
PrerollSingleAd
AdPodInfo
);
when
(
mockPrerollSingleAd
.
getAdPodInfo
()).
thenReturn
(
mockAdPodInfo
);
}
}
private
static
AdEvent
getAdEvent
(
AdEventType
adEventType
,
@Nullable
Ad
ad
)
{
private
static
AdEvent
getAdEvent
(
AdEventType
adEventType
,
@Nullable
Ad
ad
)
{
...
...
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