diff --git a/RELEASENOTES.md b/RELEASENOTES.md index b4e877f5dc..d99656b5a9 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -1,5 +1,10 @@ # Release notes # +### 2.11.7 (2020-06-29) ### + +* IMA extension: Fix the way postroll "content complete" notifications are + handled to avoid repeatedly refreshing the timeline after playback ends. + ### 2.11.6 (2020-06-24) ### * UI: Prevent `PlayerView` from temporarily hiding the video surface when diff --git a/constants.gradle b/constants.gradle index 7b6298e9d4..9d38d82369 100644 --- a/constants.gradle +++ b/constants.gradle @@ -13,8 +13,8 @@ // limitations under the License. project.ext { // ExoPlayer version and version code. - releaseVersion = '2.11.6' - releaseVersionCode = 2011006 + releaseVersion = '2.11.7' + releaseVersionCode = 2011007 minSdkVersion = 16 appTargetSdkVersion = 29 targetSdkVersion = 28 // TODO: Bump once b/143232359 is resolved diff --git a/extensions/ima/src/main/java/com/google/android/exoplayer2/ext/ima/ImaAdsLoader.java b/extensions/ima/src/main/java/com/google/android/exoplayer2/ext/ima/ImaAdsLoader.java index eecce40314..5436bb192c 100644 --- a/extensions/ima/src/main/java/com/google/android/exoplayer2/ext/ima/ImaAdsLoader.java +++ b/extensions/ima/src/main/java/com/google/android/exoplayer2/ext/ima/ImaAdsLoader.java @@ -1026,7 +1026,7 @@ public final class ImaAdsLoader implements Player.EventListener, AdsLoader { if (imaAdState == IMA_AD_STATE_NONE && playbackState == Player.STATE_BUFFERING && playWhenReady) { - checkForContentComplete(); + ensureSentContentCompleteIfAtEndOfStream(); } else if (imaAdState != IMA_AD_STATE_NONE && playbackState == Player.STATE_ENDED) { AdMediaInfo adMediaInfo = Assertions.checkNotNull(imaAdMediaInfo); if (adMediaInfo == null) { @@ -1048,15 +1048,8 @@ public final class ImaAdsLoader implements Player.EventListener, AdsLoader { return; } if (!playingAd && !player.isPlayingAd()) { - checkForContentComplete(); - if (sentContentComplete) { - for (int i = 0; i < adPlaybackState.adGroupCount; i++) { - if (adPlaybackState.adGroupTimesUs[i] != C.TIME_END_OF_SOURCE) { - adPlaybackState = adPlaybackState.withSkippedAdGroup(/* adGroupIndex= */ i); - } - } - updateAdPlaybackState(); - } else if (!timeline.isEmpty()) { + ensureSentContentCompleteIfAtEndOfStream(); + if (!sentContentComplete && !timeline.isEmpty()) { long positionMs = getContentPeriodPositionMs(player, timeline, period); timeline.getPeriod(/* periodIndex= */ 0, period); int newAdGroupIndex = period.getAdGroupIndexForPositionUs(C.msToUs(positionMs)); @@ -1090,11 +1083,7 @@ public final class ImaAdsLoader implements Player.EventListener, AdsLoader { if (!sentContentComplete && !wasPlayingAd && playingAd && imaAdState == IMA_AD_STATE_NONE) { int adGroupIndex = player.getCurrentAdGroupIndex(); if (adPlaybackState.adGroupTimesUs[adGroupIndex] == C.TIME_END_OF_SOURCE) { - adsLoader.contentComplete(); - if (DEBUG) { - Log.d(TAG, "adsLoader.contentComplete from period transition"); - } - sentContentComplete = true; + sendContentComplete(); } else { // IMA hasn't called playAd yet, so fake the content position. fakeContentProgressElapsedRealtimeMs = SystemClock.elapsedRealtime(); @@ -1212,20 +1201,31 @@ public final class ImaAdsLoader implements Player.EventListener, AdsLoader { updateAdPlaybackState(); } - private void checkForContentComplete() { - long positionMs = getContentPeriodPositionMs(Assertions.checkNotNull(player), timeline, period); + private void ensureSentContentCompleteIfAtEndOfStream() { if (!sentContentComplete && contentDurationMs != C.TIME_UNSET && pendingContentPositionMs == C.TIME_UNSET - && positionMs + THRESHOLD_END_OF_CONTENT_MS >= contentDurationMs) { - adsLoader.contentComplete(); - if (DEBUG) { - Log.d(TAG, "adsLoader.contentComplete from content position check"); - } - sentContentComplete = true; + && getContentPeriodPositionMs(Assertions.checkNotNull(player), timeline, period) + + THRESHOLD_END_OF_CONTENT_MS + >= contentDurationMs) { + sendContentComplete(); } } + private void sendContentComplete() { + adsLoader.contentComplete(); + sentContentComplete = true; + if (DEBUG) { + Log.d(TAG, "adsLoader.contentComplete"); + } + for (int i = 0; i < adPlaybackState.adGroupCount; i++) { + if (adPlaybackState.adGroupTimesUs[i] != C.TIME_END_OF_SOURCE) { + adPlaybackState = adPlaybackState.withSkippedAdGroup(/* adGroupIndex= */ i); + } + } + updateAdPlaybackState(); + } + private void updateAdPlaybackState() { // Ignore updates while detached. When a player is attached it will receive the latest state. if (eventListener != null) { diff --git a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerLibraryInfo.java b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerLibraryInfo.java index f35c3ac498..35b6199cd3 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerLibraryInfo.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerLibraryInfo.java @@ -29,11 +29,11 @@ public final class ExoPlayerLibraryInfo { /** The version of the library expressed as a string, for example "1.2.3". */ // Intentionally hardcoded. Do not derive from other constants (e.g. VERSION_INT) or vice versa. - public static final String VERSION = "2.11.6"; + public static final String VERSION = "2.11.7"; /** The version of the library expressed as {@code "ExoPlayerLib/" + VERSION}. */ // Intentionally hardcoded. Do not derive from other constants (e.g. VERSION) or vice versa. - public static final String VERSION_SLASHY = "ExoPlayerLib/2.11.6"; + public static final String VERSION_SLASHY = "ExoPlayerLib/2.11.7"; /** * The version of the library expressed as an integer, for example 1002003. @@ -43,7 +43,7 @@ public final class ExoPlayerLibraryInfo { * integer version 123045006 (123-045-006). */ // Intentionally hardcoded. Do not derive from other constants (e.g. VERSION) or vice versa. - public static final int VERSION_INT = 2011006; + public static final int VERSION_INT = 2011007; /** * Whether the library was compiled with {@link com.google.android.exoplayer2.util.Assertions}