Skip to content

Commit

Permalink
Handle LOG AdEvents for ad group load errors.
Browse files Browse the repository at this point in the history
If IMA loads an empty VAST document for an ad group it notifies via a LOG
AdEvent. Handle the event by updating the AdPlaybackState accordingly.

The error state will be handled in ExoPlayerImplInternal in a separate change.

Issue: #3584

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=184516585
  • Loading branch information
andrewlewis committed Feb 8, 2018
1 parent 901dd19 commit aa63ad3
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 14 deletions.
2 changes: 2 additions & 0 deletions RELEASENOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@
* CacheDataSource: Check periodically if it's possible to read from/write to
cache after deciding to bypass cache.
* IMA extension:
* Fix the player getting stuck when an ad group fails to load
([#3584](https://github.com/google/ExoPlayer/issues/3584)).
* Work around loadAd not being called beore the LOADED AdEvent arrives
([#3552](https://github.com/google/ExoPlayer/issues/3552)).
* Add support for playing non-Extractor content MediaSources in
Expand Down
10 changes: 10 additions & 0 deletions demos/main/src/main/assets/media.exolist.json
Original file line number Diff line number Diff line change
Expand Up @@ -566,6 +566,16 @@
"name": "VMAP pre-roll single ad, mid-roll standard pods with 5 ads every 10 seconds for 1:40, post-roll single ad",
"uri": "https://storage.googleapis.com/exoplayer-test-media-1/mkv/android-screens-lavf-56.36.100-aac-avc-main-1280x720.mkv",
"ad_tag_uri": "https://pubads.g.doubleclick.net/gampad/ads?sz=640x480&iu=/124319096/external/ad_rule_samples&ciu_szs=300x250&ad_rule=1&impl=s&gdfp_req=1&env=vp&output=vmap&unviewed_position_start=1&cust_params=deployment%3Ddevsite%26sample_ar%3Dpremidpostlongpod&cmsid=496&vid=short_tencue&correlator="
},
{
"name": "VMAP empty midroll",
"uri": "https://storage.googleapis.com/exoplayer-test-media-1/mkv/android-screens-lavf-56.36.100-aac-avc-main-1280x720.mkv",
"ad_tag_uri": "http://vastsynthesizer.appspot.com/empty-midroll"
},
{
"name": "VMAP full, empty, full midrolls",
"uri": "https://storage.googleapis.com/exoplayer-test-media-1/mkv/android-screens-lavf-56.36.100-aac-avc-main-1280x720.mkv",
"ad_tag_uri": "http://vastsynthesizer.appspot.com/empty-midroll-2"
}
]
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -516,6 +516,9 @@ public void onAdEvent(AdEvent adEvent) {
case LOG:
Map<String, String> adData = adEvent.getAdData();
Log.i(TAG, "Log AdEvent: " + adData);
if ("adLoadError".equals(adData.get("type"))) {
handleAdGroupLoadError();
}
break;
case ALL_ADS_COMPLETED:
default:
Expand Down Expand Up @@ -894,6 +897,23 @@ private void stopAdInternal() {
}
}

private void handleAdGroupLoadError() {
AdPlaybackState.AdGroup adGroup = adPlaybackState.adGroups[expectedAdGroupIndex];
// Ad group load error can be notified more than once, so check if it was already handled.
// TODO: Update the expected ad group index based on the position returned by
// getContentProgress so that it's possible to detect when more than one ad group fails to load
// consecutively.
if (adGroup.count == C.LENGTH_UNSET
|| adGroup.states[0] == AdPlaybackState.AD_STATE_UNAVAILABLE) {
if (DEBUG) {
Log.d(TAG, "Removing ad group " + expectedAdGroupIndex + " as it failed to load");
}
adPlaybackState = adPlaybackState.withAdCount(expectedAdGroupIndex, 1);
adPlaybackState = adPlaybackState.withAdLoadError(expectedAdGroupIndex, 0);
updateAdPlaybackState();
}
}

private void checkForContentComplete() {
if (contentDurationMs != C.TIME_UNSET && pendingContentPositionMs == C.TIME_UNSET
&& player.getContentPosition() + END_OF_CONTENT_POSITION_THRESHOLD_MS >= contentDurationMs
Expand Down Expand Up @@ -939,6 +959,21 @@ private int getAdGroupIndexForPosition(long[] adGroupTimesUs, long playbackPosit
return adGroupTimesUs.length == 0 ? C.INDEX_UNSET : (adGroupTimesUs.length - 1);
}

/**
* 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 static long[] getAdGroupTimesUs(List<Float> cuePoints) {
if (cuePoints.isEmpty()) {
// If no cue points are specified, there is a preroll ad.
Expand All @@ -955,18 +990,4 @@ private static long[] getAdGroupTimesUs(List<Float> cuePoints) {
return adGroupTimesUs;
}

/**
* 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;
}
}

0 comments on commit aa63ad3

Please sign in to comment.