Commit 1a5b304b by Oliver Woodman Committed by GitHub

Merge pull request #7517 from google/dev-v2-r2.11.6

r2.11.6
parents 6026b919 67c99e1d
# Release notes # # Release notes #
### 2.11.6 (2020-06-24) ###
* UI: Prevent `PlayerView` from temporarily hiding the video surface when
seeking to an unprepared period within the current window. For example when
seeking over an ad group, or to the next period in a multi-period DASH
stream ([#5507](https://github.com/google/ExoPlayer/issues/5507)).
* IMA extension:
* Add option to skip ads before the start position.
* Catch unexpected errors in `stopAd` to avoid a crash
([#7492](https://github.com/google/ExoPlayer/issues/7492)).
* Fix a bug that caused playback to be stuck buffering on resuming from
the background after all ads had played to the end
([#7508](https://github.com/google/ExoPlayer/issues/7508)).
* Fix a bug where the number of ads in an ad group couldn't change
([#7477](https://github.com/google/ExoPlayer/issues/7477)).
* Work around unexpected `pauseAd`/`stopAd` for ads that have preloaded
on seeking to another position
([#7492](https://github.com/google/ExoPlayer/issues/7492)).
* Fix incorrect rounding of ad cue points.
* Fix handling of postrolls preloading
([#7518](https://github.com/google/ExoPlayer/issues/7518)).
### 2.11.5 (2020-06-05) ### ### 2.11.5 (2020-06-05) ###
* Improve the smoothness of video playback immediately after starting, seeking * Improve the smoothness of video playback immediately after starting, seeking
...@@ -16,10 +38,10 @@ ...@@ -16,10 +38,10 @@
([#7306](https://github.com/google/ExoPlayer/issues/7306)). ([#7306](https://github.com/google/ExoPlayer/issues/7306)).
* Fix issue in `AudioTrackPositionTracker` that could cause negative positions * Fix issue in `AudioTrackPositionTracker` that could cause negative positions
to be reported at the start of playback and immediately after seeking to be reported at the start of playback and immediately after seeking
([#7456](https://github.com/google/ExoPlayer/issues/7456). ([#7456](https://github.com/google/ExoPlayer/issues/7456)).
* Fix further cases where downloads would sometimes not resume after their * Fix further cases where downloads would sometimes not resume after their
network requirements are met network requirements are met
([#7453](https://github.com/google/ExoPlayer/issues/7453). ([#7453](https://github.com/google/ExoPlayer/issues/7453)).
* DASH: * DASH:
* Merge trick play adaptation sets (i.e., adaptation sets marked with * Merge trick play adaptation sets (i.e., adaptation sets marked with
`http://dashif.org/guidelines/trickmode`) into the same `TrackGroup` as `http://dashif.org/guidelines/trickmode`) into the same `TrackGroup` as
...@@ -99,10 +121,10 @@ ...@@ -99,10 +121,10 @@
`DefaultAudioSink` constructor `DefaultAudioSink` constructor
([#7134](https://github.com/google/ExoPlayer/issues/7134)). ([#7134](https://github.com/google/ExoPlayer/issues/7134)).
* Workaround issue that could cause slower than realtime playback of AAC on * Workaround issue that could cause slower than realtime playback of AAC on
Android 10 ([#6671](https://github.com/google/ExoPlayer/issues/6671). Android 10 ([#6671](https://github.com/google/ExoPlayer/issues/6671)).
* Fix case where another app spuriously holding transient audio focus could * Fix case where another app spuriously holding transient audio focus could
prevent ExoPlayer from acquiring audio focus for an indefinite period of prevent ExoPlayer from acquiring audio focus for an indefinite period of
time ([#7182](https://github.com/google/ExoPlayer/issues/7182). time ([#7182](https://github.com/google/ExoPlayer/issues/7182)).
* Fix case where the player volume could be permanently ducked if audio focus * Fix case where the player volume could be permanently ducked if audio focus
was released whilst ducking. was released whilst ducking.
* Fix playback of WAV files with trailing non-media bytes * Fix playback of WAV files with trailing non-media bytes
...@@ -1022,7 +1044,7 @@ ...@@ -1022,7 +1044,7 @@
([#4492](https://github.com/google/ExoPlayer/issues/4492) and ([#4492](https://github.com/google/ExoPlayer/issues/4492) and
[#4634](https://github.com/google/ExoPlayer/issues/4634)). [#4634](https://github.com/google/ExoPlayer/issues/4634)).
* Fix issue where removing looping media from a playlist throws an exception * Fix issue where removing looping media from a playlist throws an exception
([#4871](https://github.com/google/ExoPlayer/issues/4871). ([#4871](https://github.com/google/ExoPlayer/issues/4871)).
* Fix issue where the preferred audio or text track would not be selected if * Fix issue where the preferred audio or text track would not be selected if
mapped onto a secondary renderer of the corresponding type mapped onto a secondary renderer of the corresponding type
([#4711](http://github.com/google/ExoPlayer/issues/4711)). ([#4711](http://github.com/google/ExoPlayer/issues/4711)).
...@@ -1439,7 +1461,7 @@ ...@@ -1439,7 +1461,7 @@
resources when the playback thread has quit by the time the loading task has resources when the playback thread has quit by the time the loading task has
completed. completed.
* ID3: Better handle malformed ID3 data * ID3: Better handle malformed ID3 data
([#3792](https://github.com/google/ExoPlayer/issues/3792). ([#3792](https://github.com/google/ExoPlayer/issues/3792)).
* Support 14-bit mode and little endianness in DTS PES packets * Support 14-bit mode and little endianness in DTS PES packets
([#3340](https://github.com/google/ExoPlayer/issues/3340)). ([#3340](https://github.com/google/ExoPlayer/issues/3340)).
* Demo app: Add ability to download not DRM protected content. * Demo app: Add ability to download not DRM protected content.
......
...@@ -13,8 +13,8 @@ ...@@ -13,8 +13,8 @@
// limitations under the License. // limitations under the License.
project.ext { project.ext {
// ExoPlayer version and version code. // ExoPlayer version and version code.
releaseVersion = '2.11.5' releaseVersion = '2.11.6'
releaseVersionCode = 2011005 releaseVersionCode = 2011006
minSdkVersion = 16 minSdkVersion = 16
appTargetSdkVersion = 29 appTargetSdkVersion = 29
targetSdkVersion = 28 // TODO: Bump once b/143232359 is resolved targetSdkVersion = 28 // TODO: Bump once b/143232359 is resolved
......
...@@ -476,6 +476,11 @@ ...@@ -476,6 +476,11 @@
"name": "VMAP full, empty, full midrolls", "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", "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://vastsynthesizer.appspot.com/empty-midroll-2" "ad_tag_uri": "https://vastsynthesizer.appspot.com/empty-midroll-2"
},
{
"name": "VMAP midroll at 1765 s",
"uri": "https://storage.googleapis.com/exoplayer-test-media-1/mp4/frame-counter-one-hour.mp4",
"ad_tag_uri": "https://vastsynthesizer.appspot.com/midroll-large"
} }
] ]
}, },
......
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.exoplayer2.ext.ima;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.source.ads.AdPlaybackState;
import java.util.Arrays;
import java.util.List;
/**
* Static utility class for constructing {@link AdPlaybackState} instances from IMA-specific data.
*/
/* package */ final class AdPlaybackStateFactory {
private AdPlaybackStateFactory() {}
/**
* Construct an {@link AdPlaybackState} from the provided {@code cuePoints}.
*
* @param cuePoints The cue points of the ads in seconds.
* @return The {@link AdPlaybackState}.
*/
public static AdPlaybackState fromCuePoints(List<Float> cuePoints) {
if (cuePoints.isEmpty()) {
// If no cue points are specified, there is a preroll ad.
return new AdPlaybackState(/* adGroupTimesUs...= */ 0);
}
int count = cuePoints.size();
long[] adGroupTimesUs = new long[count];
int adGroupIndex = 0;
for (int i = 0; i < count; i++) {
double cuePoint = cuePoints.get(i);
if (cuePoint == -1.0) {
adGroupTimesUs[count - 1] = C.TIME_END_OF_SOURCE;
} else {
adGroupTimesUs[adGroupIndex++] = Math.round(C.MICROS_PER_SECOND * cuePoint);
}
}
// Cue points may be out of order, so sort them.
Arrays.sort(adGroupTimesUs, 0, adGroupIndex);
return new AdPlaybackState(adGroupTimesUs);
}
}
...@@ -29,11 +29,11 @@ public final class ExoPlayerLibraryInfo { ...@@ -29,11 +29,11 @@ public final class ExoPlayerLibraryInfo {
/** The version of the library expressed as a string, for example "1.2.3". */ /** 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. // Intentionally hardcoded. Do not derive from other constants (e.g. VERSION_INT) or vice versa.
public static final String VERSION = "2.11.5"; public static final String VERSION = "2.11.6";
/** The version of the library expressed as {@code "ExoPlayerLib/" + VERSION}. */ /** The version of the library expressed as {@code "ExoPlayerLib/" + VERSION}. */
// Intentionally hardcoded. Do not derive from other constants (e.g. VERSION) or vice versa. // Intentionally hardcoded. Do not derive from other constants (e.g. VERSION) or vice versa.
public static final String VERSION_SLASHY = "ExoPlayerLib/2.11.5"; public static final String VERSION_SLASHY = "ExoPlayerLib/2.11.6";
/** /**
* The version of the library expressed as an integer, for example 1002003. * The version of the library expressed as an integer, for example 1002003.
...@@ -43,7 +43,7 @@ public final class ExoPlayerLibraryInfo { ...@@ -43,7 +43,7 @@ public final class ExoPlayerLibraryInfo {
* integer version 123045006 (123-045-006). * integer version 123045006 (123-045-006).
*/ */
// Intentionally hardcoded. Do not derive from other constants (e.g. VERSION) or vice versa. // Intentionally hardcoded. Do not derive from other constants (e.g. VERSION) or vice versa.
public static final int VERSION_INT = 2011005; public static final int VERSION_INT = 2011006;
/** /**
* Whether the library was compiled with {@link com.google.android.exoplayer2.util.Assertions} * Whether the library was compiled with {@link com.google.android.exoplayer2.util.Assertions}
......
...@@ -124,13 +124,9 @@ public final class AdPlaybackState { ...@@ -124,13 +124,9 @@ public final class AdPlaybackState {
return result; return result;
} }
/** /** Returns a new instance with the ad count set to {@code count}. */
* Returns a new instance with the ad count set to {@code count}. This method may only be called
* if this instance's ad count has not yet been specified.
*/
@CheckResult @CheckResult
public AdGroup withAdCount(int count) { public AdGroup withAdCount(int count) {
Assertions.checkArgument(this.count == C.LENGTH_UNSET && states.length <= count);
@AdState int[] states = copyStatesWithSpaceForAdCount(this.states, count); @AdState int[] states = copyStatesWithSpaceForAdCount(this.states, count);
long[] durationsUs = copyDurationsUsWithSpaceForAdCount(this.durationsUs, count); long[] durationsUs = copyDurationsUsWithSpaceForAdCount(this.durationsUs, count);
@NullableType Uri[] uris = Arrays.copyOf(this.uris, count); @NullableType Uri[] uris = Arrays.copyOf(this.uris, count);
...@@ -139,17 +135,11 @@ public final class AdPlaybackState { ...@@ -139,17 +135,11 @@ public final class AdPlaybackState {
/** /**
* Returns a new instance with the specified {@code uri} set for the specified ad, and the ad * Returns a new instance with the specified {@code uri} set for the specified ad, and the ad
* marked as {@link #AD_STATE_AVAILABLE}. The specified ad must currently be in {@link * marked as {@link #AD_STATE_AVAILABLE}.
* #AD_STATE_UNAVAILABLE}, which is the default state.
*
* <p>This instance's ad count may be unknown, in which case {@code index} must be less than the
* ad count specified later. Otherwise, {@code index} must be less than the current ad count.
*/ */
@CheckResult @CheckResult
public AdGroup withAdUri(Uri uri, int index) { public AdGroup withAdUri(Uri uri, int index) {
Assertions.checkArgument(count == C.LENGTH_UNSET || index < count);
@AdState int[] states = copyStatesWithSpaceForAdCount(this.states, index + 1); @AdState int[] states = copyStatesWithSpaceForAdCount(this.states, index + 1);
Assertions.checkArgument(states[index] == AD_STATE_UNAVAILABLE);
long[] durationsUs = long[] durationsUs =
this.durationsUs.length == states.length this.durationsUs.length == states.length
? this.durationsUs ? this.durationsUs
...@@ -280,7 +270,9 @@ public final class AdPlaybackState { ...@@ -280,7 +270,9 @@ public final class AdPlaybackState {
public final AdGroup[] adGroups; public final AdGroup[] adGroups;
/** The position offset in the first unplayed ad at which to begin playback, in microseconds. */ /** The position offset in the first unplayed ad at which to begin playback, in microseconds. */
public final long adResumePositionUs; public final long adResumePositionUs;
/** The content duration in microseconds, if known. {@link C#TIME_UNSET} otherwise. */ /**
* The duration of the content period in microseconds, if known. {@link C#TIME_UNSET} otherwise.
*/
public final long contentDurationUs; public final long contentDurationUs;
/** /**
...@@ -489,6 +481,54 @@ public final class AdPlaybackState { ...@@ -489,6 +481,54 @@ public final class AdPlaybackState {
return result; return result;
} }
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("AdPlaybackState(adResumePositionUs=");
sb.append(adResumePositionUs);
sb.append(", adGroups=[");
for (int i = 0; i < adGroups.length; i++) {
sb.append("adGroup(timeUs=");
sb.append(adGroupTimesUs[i]);
sb.append(", ads=[");
for (int j = 0; j < adGroups[i].states.length; j++) {
sb.append("ad(state=");
switch (adGroups[i].states[j]) {
case AD_STATE_UNAVAILABLE:
sb.append('_');
break;
case AD_STATE_ERROR:
sb.append('!');
break;
case AD_STATE_AVAILABLE:
sb.append('R');
break;
case AD_STATE_PLAYED:
sb.append('P');
break;
case AD_STATE_SKIPPED:
sb.append('S');
break;
default:
sb.append('?');
break;
}
sb.append(", durationUs=");
sb.append(adGroups[i].durationsUs[j]);
sb.append(')');
if (j < adGroups[i].states.length - 1) {
sb.append(", ");
}
}
sb.append("])");
if (i < adGroups.length - 1) {
sb.append(", ");
}
}
sb.append("])");
return sb.toString();
}
private boolean isPositionBeforeAdGroup( private boolean isPositionBeforeAdGroup(
long positionUs, long periodDurationUs, int adGroupIndex) { long positionUs, long periodDurationUs, int adGroupIndex) {
if (positionUs == C.TIME_END_OF_SOURCE) { if (positionUs == C.TIME_END_OF_SOURCE) {
......
...@@ -44,23 +44,16 @@ public final class SinglePeriodAdTimeline extends ForwardingTimeline { ...@@ -44,23 +44,16 @@ public final class SinglePeriodAdTimeline extends ForwardingTimeline {
@Override @Override
public Period getPeriod(int periodIndex, Period period, boolean setIds) { public Period getPeriod(int periodIndex, Period period, boolean setIds) {
timeline.getPeriod(periodIndex, period, setIds); timeline.getPeriod(periodIndex, period, setIds);
long durationUs =
period.durationUs == C.TIME_UNSET ? adPlaybackState.contentDurationUs : period.durationUs;
period.set( period.set(
period.id, period.id,
period.uid, period.uid,
period.windowIndex, period.windowIndex,
period.durationUs, durationUs,
period.getPositionInWindowUs(), period.getPositionInWindowUs(),
adPlaybackState); adPlaybackState);
return period; return period;
} }
@Override
public Window getWindow(int windowIndex, Window window, long defaultPositionProjectionUs) {
window = super.getWindow(windowIndex, window, defaultPositionProjectionUs);
if (window.durationUs == C.TIME_UNSET) {
window.durationUs = adPlaybackState.contentDurationUs;
}
return window;
}
} }
...@@ -155,12 +155,22 @@ public final class MimeTypes { ...@@ -155,12 +155,22 @@ public final class MimeTypes {
if (mimeType == null) { if (mimeType == null) {
return false; return false;
} }
// TODO: Consider adding additional audio MIME types here. // TODO: Add additional audio MIME types. Also consider evaluating based on Format rather than
// just MIME type, since in some cases the property is true for a subset of the profiles
// belonging to a single MIME type. If we do this, we should move the method to a different
// class. See [Internal ref: http://go/exo-audio-format-random-access].
switch (mimeType) { switch (mimeType) {
case AUDIO_AAC:
case AUDIO_MPEG: case AUDIO_MPEG:
case AUDIO_MPEG_L1: case AUDIO_MPEG_L1:
case AUDIO_MPEG_L2: case AUDIO_MPEG_L2:
case AUDIO_RAW:
case AUDIO_ALAW:
case AUDIO_MLAW:
case AUDIO_OPUS:
case AUDIO_FLAC:
case AUDIO_AC3:
case AUDIO_E_AC3:
case AUDIO_E_AC3_JOC:
return true; return true;
default: default:
return false; return false;
......
...@@ -48,6 +48,8 @@ import com.google.android.exoplayer2.ExoPlaybackException; ...@@ -48,6 +48,8 @@ import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.PlaybackPreparer; import com.google.android.exoplayer2.PlaybackPreparer;
import com.google.android.exoplayer2.Player; import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.Player.DiscontinuityReason; import com.google.android.exoplayer2.Player.DiscontinuityReason;
import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.Timeline.Period;
import com.google.android.exoplayer2.metadata.Metadata; import com.google.android.exoplayer2.metadata.Metadata;
import com.google.android.exoplayer2.metadata.flac.PictureFrame; import com.google.android.exoplayer2.metadata.flac.PictureFrame;
import com.google.android.exoplayer2.metadata.id3.ApicFrame; import com.google.android.exoplayer2.metadata.id3.ApicFrame;
...@@ -1506,6 +1508,13 @@ public class PlayerView extends FrameLayout implements AdsLoader.AdViewProvider ...@@ -1506,6 +1508,13 @@ public class PlayerView extends FrameLayout implements AdsLoader.AdViewProvider
SingleTapListener, SingleTapListener,
PlayerControlView.VisibilityListener { PlayerControlView.VisibilityListener {
private final Period period;
private @Nullable Object lastPeriodUidWithTracks;
public ComponentListener() {
period = new Period();
}
// TextOutput implementation // TextOutput implementation
@Override @Override
...@@ -1554,6 +1563,29 @@ public class PlayerView extends FrameLayout implements AdsLoader.AdViewProvider ...@@ -1554,6 +1563,29 @@ public class PlayerView extends FrameLayout implements AdsLoader.AdViewProvider
@Override @Override
public void onTracksChanged(TrackGroupArray tracks, TrackSelectionArray selections) { public void onTracksChanged(TrackGroupArray tracks, TrackSelectionArray selections) {
// Suppress the update if transitioning to an unprepared period within the same window. This
// is necessary to avoid closing the shutter when such a transition occurs. See:
// https://github.com/google/ExoPlayer/issues/5507.
Player player = Assertions.checkNotNull(PlayerView.this.player);
Timeline timeline = player.getCurrentTimeline();
if (timeline.isEmpty()) {
lastPeriodUidWithTracks = null;
} else if (!player.getCurrentTrackGroups().isEmpty()) {
lastPeriodUidWithTracks =
timeline.getPeriod(player.getCurrentPeriodIndex(), period, /* setIds= */ true).uid;
} else if (lastPeriodUidWithTracks != null) {
int lastPeriodIndexWithTracks = timeline.getIndexOfPeriod(lastPeriodUidWithTracks);
if (lastPeriodIndexWithTracks != C.INDEX_UNSET) {
int lastWindowIndexWithTracks =
timeline.getPeriod(lastPeriodIndexWithTracks, period).windowIndex;
if (player.getCurrentWindowIndex() == lastWindowIndexWithTracks) {
// We're in the same window. Suppress the update.
return;
}
}
lastPeriodUidWithTracks = null;
}
updateForCurrentTrackSelections(/* isNewPlayer= */ false); updateForCurrentTrackSelections(/* isNewPlayer= */ false);
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment