Fix content progress updates and position faking

Occasionally the player could transition from playing content to playing an ad
after IMA called playAd. The discontinuity triggered faking the content
position, and the fake position was passed to IMA when content resumed causing
the wrong ad group to be loaded. Fix this by only faking the position if the
player transitions before playAd.

Also fix the calculation of the expected ad group index for a postroll ad, and
wait for the player to transition back from ad to content before passing a
content progress update.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=185994229
This commit is contained in:
andrewlewis 2018-02-16 07:36:08 -08:00 committed by Oliver Woodman
parent 6c3b677ddb
commit 56c9d023fa

View File

@ -252,10 +252,9 @@ public final class ImaAdsLoader extends Player.DefaultEventListener implements A
*/ */
private boolean shouldNotifyAdPrepareError; private boolean shouldNotifyAdPrepareError;
/** /**
* If a content period has finished but IMA has not yet sent an ad event with {@link * If a content period has finished but IMA has not yet called {@link #playAd()}, stores the value
* AdEvent.AdEventType#CONTENT_PAUSE_REQUESTED}, stores the value of {@link * of {@link SystemClock#elapsedRealtime()} when the content stopped playing. This can be used to
* SystemClock#elapsedRealtime()} when the content stopped playing. This can be used to determine * determine a fake, increasing content position. {@link C#TIME_UNSET} otherwise.
* a fake, increasing content position. {@link C#TIME_UNSET} otherwise.
*/ */
private long fakeContentProgressElapsedRealtimeMs; private long fakeContentProgressElapsedRealtimeMs;
/** /**
@ -541,18 +540,21 @@ public final class ImaAdsLoader extends Player.DefaultEventListener implements A
contentPositionMs = fakeContentProgressOffsetMs + elapsedSinceEndMs; contentPositionMs = fakeContentProgressOffsetMs + elapsedSinceEndMs;
expectedAdGroupIndex = expectedAdGroupIndex =
adPlaybackState.getAdGroupIndexForPositionUs(C.msToUs(contentPositionMs)); adPlaybackState.getAdGroupIndexForPositionUs(C.msToUs(contentPositionMs));
} else if (imaAdState == IMA_AD_STATE_NONE && hasContentDuration) { } else if (imaAdState == IMA_AD_STATE_NONE && !playingAd && hasContentDuration) {
contentPositionMs = player.getCurrentPosition(); contentPositionMs = player.getCurrentPosition();
// Update the expected ad group index for the current content position. The update is delayed // 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 // 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. // just after an ad group isn't incorrectly attributed to the next ad group.
int nextAdGroupIndex = int nextAdGroupIndex =
adPlaybackState.getAdGroupIndexAfterPositionUs(C.msToUs(contentPositionMs)); adPlaybackState.getAdGroupIndexAfterPositionUs(C.msToUs(contentPositionMs));
if (nextAdGroupIndex != expectedAdGroupIndex if (nextAdGroupIndex != expectedAdGroupIndex && nextAdGroupIndex != C.INDEX_UNSET) {
&& nextAdGroupIndex != C.INDEX_UNSET long nextAdGroupTimeMs = C.usToMs(adPlaybackState.adGroupTimesUs[nextAdGroupIndex]);
&& C.usToMs(adPlaybackState.adGroupTimesUs[nextAdGroupIndex]) - contentPositionMs if (nextAdGroupTimeMs == C.TIME_END_OF_SOURCE) {
< MAXIMUM_PRELOAD_DURATION_MS) { nextAdGroupTimeMs = contentDurationMs;
expectedAdGroupIndex = nextAdGroupIndex; }
if (nextAdGroupTimeMs - contentPositionMs < MAXIMUM_PRELOAD_DURATION_MS) {
expectedAdGroupIndex = nextAdGroupIndex;
}
} }
} else { } else {
return VideoProgressUpdate.VIDEO_TIME_NOT_READY; return VideoProgressUpdate.VIDEO_TIME_NOT_READY;
@ -567,12 +569,12 @@ public final class ImaAdsLoader extends Player.DefaultEventListener implements A
public VideoProgressUpdate getAdProgress() { public VideoProgressUpdate getAdProgress() {
if (player == null) { if (player == null) {
return lastAdProgress; return lastAdProgress;
} else if (imaAdState == IMA_AD_STATE_NONE) { } else if (imaAdState != IMA_AD_STATE_NONE && playingAd) {
return VideoProgressUpdate.VIDEO_TIME_NOT_READY;
} else {
long adDuration = player.getDuration(); long adDuration = player.getDuration();
return adDuration == C.TIME_UNSET ? VideoProgressUpdate.VIDEO_TIME_NOT_READY return adDuration == C.TIME_UNSET ? VideoProgressUpdate.VIDEO_TIME_NOT_READY
: new VideoProgressUpdate(player.getCurrentPosition(), adDuration); : new VideoProgressUpdate(player.getCurrentPosition(), adDuration);
} else {
return VideoProgressUpdate.VIDEO_TIME_NOT_READY;
} }
} }
@ -625,6 +627,9 @@ public final class ImaAdsLoader extends Player.DefaultEventListener implements A
Log.w(TAG, "Unexpected playAd without stopAd"); Log.w(TAG, "Unexpected playAd without stopAd");
break; break;
case IMA_AD_STATE_NONE: case IMA_AD_STATE_NONE:
// IMA is requesting to play the ad, so stop faking the content position.
fakeContentProgressElapsedRealtimeMs = C.TIME_UNSET;
fakeContentProgressOffsetMs = C.TIME_UNSET;
imaAdState = IMA_AD_STATE_PLAYING; imaAdState = IMA_AD_STATE_PLAYING;
for (int i = 0; i < adCallbacks.size(); i++) { for (int i = 0; i < adCallbacks.size(); i++) {
adCallbacks.get(i).onPlay(); adCallbacks.get(i).onPlay();
@ -923,9 +928,9 @@ public final class ImaAdsLoader extends Player.DefaultEventListener implements A
Log.d(TAG, "VideoAdPlayerCallback.onEnded in onTimelineChanged/onPositionDiscontinuity"); Log.d(TAG, "VideoAdPlayerCallback.onEnded in onTimelineChanged/onPositionDiscontinuity");
} }
} }
if (!wasPlayingAd && playingAd) { if (!wasPlayingAd && playingAd && imaAdState == IMA_AD_STATE_NONE) {
int adGroupIndex = player.getCurrentAdGroupIndex(); int adGroupIndex = player.getCurrentAdGroupIndex();
// IMA hasn't sent CONTENT_PAUSE_REQUESTED yet, so fake the content position. // IMA hasn't called playAd yet, so fake the content position.
fakeContentProgressElapsedRealtimeMs = SystemClock.elapsedRealtime(); fakeContentProgressElapsedRealtimeMs = SystemClock.elapsedRealtime();
fakeContentProgressOffsetMs = C.usToMs(adPlaybackState.adGroupTimesUs[adGroupIndex]); fakeContentProgressOffsetMs = C.usToMs(adPlaybackState.adGroupTimesUs[adGroupIndex]);
if (fakeContentProgressOffsetMs == C.TIME_END_OF_SOURCE) { if (fakeContentProgressOffsetMs == C.TIME_END_OF_SOURCE) {
@ -955,9 +960,6 @@ public final class ImaAdsLoader extends Player.DefaultEventListener implements A
pendingContentPositionMs = C.TIME_UNSET; pendingContentPositionMs = C.TIME_UNSET;
sentPendingContentPositionMs = false; sentPendingContentPositionMs = false;
} }
// IMA is requesting to pause content, so stop faking the content position.
fakeContentProgressElapsedRealtimeMs = C.TIME_UNSET;
fakeContentProgressOffsetMs = C.TIME_UNSET;
} }
private void stopAdInternal() { private void stopAdInternal() {