Skip to content

Commit

Permalink
Add timeout for ad to load.
Browse files Browse the repository at this point in the history
In some cases, the IMA SDK fails to call the expected loadAd
event to load the next ad to play. This is (potentially) the
only remaining case where playback can get stuck due to missing
calls from IMA as the player doesn't even have a MediaSource at
this stage and is only waiting for IMA to provide the ad URL.

We can reuse the existing adPreloadTimeoutMs that was added for
a similar purpose (when preloading the first ad in the group).
The JavaDoc matches this purpose as well and the default timeout
is appropriate since we expect to get the loadAd call immediately.

Issue: #10510
PiperOrigin-RevId: 466953617
  • Loading branch information
tonihei authored and marcbaechinger committed Oct 19, 2022
1 parent 43aa89a commit 91fc5c4
Showing 1 changed file with 38 additions and 3 deletions.
Expand Up @@ -143,6 +143,7 @@
private final BiMap<AdMediaInfo, AdInfo> adInfoByAdMediaInfo;
private final AdDisplayContainer adDisplayContainer;
private final AdsLoader adsLoader;
private final Runnable adLoadTimeoutRunnable;

@Nullable private Object pendingAdRequestContext;
@Nullable private Player player;
Expand Down Expand Up @@ -256,6 +257,7 @@ public AdTagLoader(
contentDurationMs = C.TIME_UNSET;
timeline = Timeline.EMPTY;
adPlaybackState = AdPlaybackState.NONE;
adLoadTimeoutRunnable = this::handleAdLoadTimeout;
if (adViewGroup != null) {
adDisplayContainer =
imaFactory.createAdDisplayContainer(adViewGroup, /* player= */ componentListener);
Expand Down Expand Up @@ -488,7 +490,7 @@ public void onPlaybackStateChanged(@Player.State int playbackState) {

if (playbackState == Player.STATE_BUFFERING
&& !player.isPlayingAd()
&& isWaitingForAdToLoad()) {
&& isWaitingForFirstAdToPreload()) {
waitingForPreloadElapsedRealtimeMs = SystemClock.elapsedRealtime();
} else if (playbackState == Player.STATE_READY) {
waitingForPreloadElapsedRealtimeMs = C.TIME_UNSET;
Expand Down Expand Up @@ -780,7 +782,7 @@ private void resumeContentInternal() {
* Returns whether this instance is expecting the first ad in an the upcoming ad group to load
* within the {@link ImaUtil.Configuration#adPreloadTimeoutMs preload timeout}.
*/
private boolean isWaitingForAdToLoad() {
private boolean isWaitingForFirstAdToPreload() {
@Nullable Player player = this.player;
if (player == null) {
return false;
Expand All @@ -802,6 +804,23 @@ private boolean isWaitingForAdToLoad() {
return timeUntilAdMs < configuration.adPreloadTimeoutMs;
}

private boolean isWaitingForCurrentAdToLoad() {
@Nullable Player player = this.player;
if (player == null) {
return false;
}
int adGroupIndex = player.getCurrentAdGroupIndex();
if (adGroupIndex == C.INDEX_UNSET) {
return false;
}
AdPlaybackState.AdGroup adGroup = adPlaybackState.getAdGroup(adGroupIndex);
int adIndexInAdGroup = player.getCurrentAdIndexInAdGroup();
if (adGroup.count == C.LENGTH_UNSET || adGroup.count <= adIndexInAdGroup) {
return true;
}
return adGroup.states[adIndexInAdGroup] == AdPlaybackState.AD_STATE_UNAVAILABLE;
}

private void handlePlayerStateChanged(boolean playWhenReady, @Player.State int playbackState) {
if (playingAd && imaAdState == IMA_AD_STATE_PLAYING) {
if (!bufferingAd && playbackState == Player.STATE_BUFFERING) {
Expand Down Expand Up @@ -892,6 +911,10 @@ private void handleTimelineOrPositionChanged() {
}
}
}
if (isWaitingForCurrentAdToLoad()) {
handler.removeCallbacks(adLoadTimeoutRunnable);
handler.postDelayed(adLoadTimeoutRunnable, configuration.adPreloadTimeoutMs);
}
}

private void loadAdInternal(AdMediaInfo adMediaInfo, AdPodInfo adPodInfo) {
Expand All @@ -918,6 +941,12 @@ private void loadAdInternal(AdMediaInfo adMediaInfo, AdPodInfo adPodInfo) {
// timeout after its media load timeout.
return;
}
if (player != null
&& player.getCurrentAdGroupIndex() == adGroupIndex
&& player.getCurrentAdIndexInAdGroup() == adIndexInAdGroup) {
// Loaded ad info the player is currently waiting for.
handler.removeCallbacks(adLoadTimeoutRunnable);
}

// The ad count may increase on successive loads of ads in the same ad pod, for example, due to
// separate requests for ad tags with multiple ads within the ad pod completing after an earlier
Expand Down Expand Up @@ -1063,6 +1092,12 @@ private void handleAdGroupLoadError(Exception error) {
}
}

private void handleAdLoadTimeout() {
// IMA got stuck and didn't load an ad in time, so skip the entire group.
handleAdGroupLoadError(new IOException("Ad loading timed out"));
maybeNotifyPendingAdLoadError();
}

private void markAdGroupInErrorStateAndClearPendingContentPosition(int adGroupIndex) {
// Update the ad playback state so all ads in the ad group are in the error state.
AdPlaybackState.AdGroup adGroup = adPlaybackState.getAdGroup(adGroupIndex);
Expand Down Expand Up @@ -1334,7 +1369,7 @@ public VideoProgressUpdate getContentProgress() {
} else if (pendingContentPositionMs != C.TIME_UNSET
&& player != null
&& player.getPlaybackState() == Player.STATE_BUFFERING
&& isWaitingForAdToLoad()) {
&& isWaitingForFirstAdToPreload()) {
// Prepare to timeout the load of an ad for the pending seek operation.
waitingForPreloadElapsedRealtimeMs = SystemClock.elapsedRealtime();
}
Expand Down

0 comments on commit 91fc5c4

Please sign in to comment.