diff --git a/RELEASENOTES.md b/RELEASENOTES.md index b643bacdb2..645fb3a97a 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -1,5 +1,11 @@ # Release notes # +### 2.8.4 ### + +* IMA: Improve handling of consecutive empty ad groups + ([#4030](https://github.com/google/ExoPlayer/issues/4030)), + ([#4280](https://github.com/google/ExoPlayer/issues/4280)). + ### 2.8.3 ### * IMA: diff --git a/constants.gradle b/constants.gradle index b8cfc7d2a4..08510e02ae 100644 --- a/constants.gradle +++ b/constants.gradle @@ -13,8 +13,8 @@ // limitations under the License. project.ext { // ExoPlayer version and version code. - releaseVersion = '2.8.3' - releaseVersionCode = 2803 + releaseVersion = '2.8.4' + releaseVersionCode = 2804 // Important: ExoPlayer specifies a minSdkVersion of 14 because various // components provided by the library may be of use on older devices. // However, please note that the core media playback functionality provided diff --git a/demos/main/src/main/java/com/google/android/exoplayer2/demo/PlayerActivity.java b/demos/main/src/main/java/com/google/android/exoplayer2/demo/PlayerActivity.java index 565f7e300a..67c56127ee 100644 --- a/demos/main/src/main/java/com/google/android/exoplayer2/demo/PlayerActivity.java +++ b/demos/main/src/main/java/com/google/android/exoplayer2/demo/PlayerActivity.java @@ -188,6 +188,7 @@ public class PlayerActivity extends Activity @Override public void onNewIntent(Intent intent) { releasePlayer(); + releaseAdsLoader(); clearStartPosition(); setIntent(intent); } 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 dc2df9eb71..7c095ff2ef 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 @@ -376,6 +376,9 @@ public final class ImaAdsLoader extends Player.DefaultEventListener implements A adDisplayContainer.setPlayer(this); if (imaSdkSettings == null) { imaSdkSettings = imaSdkFactory.createImaSdkSettings(); + if (DEBUG) { + imaSdkSettings.setDebugMode(true); + } } imaSdkSettings.setPlayerType(IMA_SDK_SETTINGS_PLAYER_TYPE); imaSdkSettings.setPlayerVersion(IMA_SDK_SETTINGS_PLAYER_VERSION); @@ -619,8 +622,11 @@ public final class ImaAdsLoader extends Player.DefaultEventListener implements A } else if (fakeContentProgressElapsedRealtimeMs != C.TIME_UNSET) { long elapsedSinceEndMs = SystemClock.elapsedRealtime() - fakeContentProgressElapsedRealtimeMs; contentPositionMs = fakeContentProgressOffsetMs + elapsedSinceEndMs; - expectedAdGroupIndex = + int adGroupIndexForPosition = adPlaybackState.getAdGroupIndexForPositionUs(C.msToUs(contentPositionMs)); + if (adGroupIndexForPosition != C.INDEX_UNSET) { + expectedAdGroupIndex = adGroupIndexForPosition; + } } else if (imaAdState == IMA_AD_STATE_NONE && !playingAd && hasContentDuration) { contentPositionMs = player.getCurrentPosition(); // Update the expected ad group index for the current content position. The update is delayed @@ -1096,6 +1102,16 @@ public final class ImaAdsLoader extends Player.DefaultEventListener implements A if (pendingAdLoadError == null) { pendingAdLoadError = AdLoadException.createForAdGroup(error, adGroupIndex); } + // Discard the ad break, which makes sure we don't receive duplicate load error events. + adsManager.discardAdBreak(); + // Set the next expected ad group index so we can handle multiple load errors in a row. + adGroupIndex++; + if (adGroupIndex < adPlaybackState.adGroupCount) { + expectedAdGroupIndex = adGroupIndex; + } else { + expectedAdGroupIndex = C.INDEX_UNSET; + } + pendingContentPositionMs = C.TIME_UNSET; } private void handleAdPrepareError(int adGroupIndex, int adIndexInAdGroup, Exception exception) { 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 8de3385d1a..e18a897029 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.8.3"; + public static final String VERSION = "2.8.4"; /** 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.8.3"; + public static final String VERSION_SLASHY = "ExoPlayerLib/2.8.4"; /** * 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 = 2008003; + public static final int VERSION_INT = 2008004; /** * Whether the library was compiled with {@link com.google.android.exoplayer2.util.Assertions} diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/FragmentedMp4Extractor.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/FragmentedMp4Extractor.java index 74181f8898..c7ec2378a1 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/FragmentedMp4Extractor.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/FragmentedMp4Extractor.java @@ -598,10 +598,13 @@ public final class FragmentedMp4Extractor implements Extractor { // Output the sample metadata. if (segmentIndexEarliestPresentationTimeUs != C.TIME_UNSET) { + long sampleTimeUs = segmentIndexEarliestPresentationTimeUs + presentationTimeDeltaUs; + if (timestampAdjuster != null) { + sampleTimeUs = timestampAdjuster.adjustSampleTimestamp(sampleTimeUs); + } for (TrackOutput emsgTrackOutput : emsgTrackOutputs) { emsgTrackOutput.sampleMetadata( - segmentIndexEarliestPresentationTimeUs + presentationTimeDeltaUs, - C.BUFFER_FLAG_KEY_FRAME, sampleSize, 0 /* offset */, null); + sampleTimeUs, C.BUFFER_FLAG_KEY_FRAME, sampleSize, /* offset= */ 0, null); } } else { // We need the first sample timestamp in the segment before we can output the metadata. @@ -1208,6 +1211,10 @@ public final class FragmentedMp4Extractor implements Extractor { Track track = currentTrackBundle.track; TrackOutput output = currentTrackBundle.output; int sampleIndex = currentTrackBundle.currentSampleIndex; + long sampleTimeUs = fragment.getSamplePresentationTime(sampleIndex) * 1000L; + if (timestampAdjuster != null) { + sampleTimeUs = timestampAdjuster.adjustSampleTimestamp(sampleTimeUs); + } if (track.nalUnitLengthFieldLength != 0) { // Zero the top three bytes of the array that we'll use to decode nal unit lengths, in case // they're only 1 or 2 bytes long. @@ -1248,8 +1255,7 @@ public final class FragmentedMp4Extractor implements Extractor { // If the format is H.265/HEVC the NAL unit header has two bytes so skip one more byte. nalBuffer.setPosition(MimeTypes.VIDEO_H265.equals(track.format.sampleMimeType) ? 1 : 0); nalBuffer.setLimit(unescapedLength); - CeaUtil.consume(fragment.getSamplePresentationTime(sampleIndex) * 1000L, nalBuffer, - cea608TrackOutputs); + CeaUtil.consume(sampleTimeUs, nalBuffer, cea608TrackOutputs); } else { // Write the payload of the NAL unit. writtenBytes = output.sampleData(input, sampleCurrentNalBytesRemaining, false); @@ -1265,11 +1271,6 @@ public final class FragmentedMp4Extractor implements Extractor { } } - long sampleTimeUs = fragment.getSamplePresentationTime(sampleIndex) * 1000L; - if (timestampAdjuster != null) { - sampleTimeUs = timestampAdjuster.adjustSampleTimestamp(sampleTimeUs); - } - @C.BufferFlags int sampleFlags = fragment.sampleIsSyncFrameTable[sampleIndex] ? C.BUFFER_FLAG_KEY_FRAME : 0; @@ -1298,10 +1299,17 @@ public final class FragmentedMp4Extractor implements Extractor { while (!pendingMetadataSampleInfos.isEmpty()) { MetadataSampleInfo sampleInfo = pendingMetadataSampleInfos.removeFirst(); pendingMetadataSampleBytes -= sampleInfo.size; + long metadataTimeUs = sampleTimeUs + sampleInfo.presentationTimeDeltaUs; + if (timestampAdjuster != null) { + metadataTimeUs = timestampAdjuster.adjustSampleTimestamp(metadataTimeUs); + } for (TrackOutput emsgTrackOutput : emsgTrackOutputs) { emsgTrackOutput.sampleMetadata( - sampleTimeUs + sampleInfo.presentationTimeDeltaUs, - C.BUFFER_FLAG_KEY_FRAME, sampleInfo.size, pendingMetadataSampleBytes, null); + metadataTimeUs, + C.BUFFER_FLAG_KEY_FRAME, + sampleInfo.size, + pendingMetadataSampleBytes, + null); } } } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/upstream/DefaultAllocator.java b/library/core/src/main/java/com/google/android/exoplayer2/upstream/DefaultAllocator.java index d9bd5873f0..06ca83dd93 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/upstream/DefaultAllocator.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/upstream/DefaultAllocator.java @@ -118,8 +118,18 @@ public final class DefaultAllocator implements Allocator { } for (Allocation allocation : allocations) { // Weak sanity check that the allocation probably originated from this pool. - Assertions.checkArgument(allocation.data == initialAllocationBlock - || allocation.data.length == individualAllocationSize); + if (allocation.data != initialAllocationBlock + && allocation.data.length != individualAllocationSize) { + throw new IllegalArgumentException( + "Unexpected allocation: " + + System.identityHashCode(allocation.data) + + ", " + + System.identityHashCode(initialAllocationBlock) + + ", " + + allocation.data.length + + ", " + + individualAllocationSize); + } availableAllocations[availableCount++] = allocation; } allocatedCount -= allocations.length;