mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08:00
Support resuming content after ads
------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=161775394
This commit is contained in:
parent
4f2fae4fba
commit
6c74a31556
@ -406,7 +406,7 @@ public class PlayerActivity extends Activity implements OnClickListener, ExoPlay
|
||||
|
||||
private void updateResumePosition() {
|
||||
resumeWindow = player.getCurrentWindowIndex();
|
||||
resumePosition = player.isCurrentWindowSeekable() ? Math.max(0, player.getCurrentPosition())
|
||||
resumePosition = player.isCurrentWindowSeekable() ? Math.max(0, player.getContentPosition())
|
||||
: C.TIME_UNSET;
|
||||
}
|
||||
|
||||
|
@ -536,6 +536,11 @@ public final class DynamicConcatenatingMediaSourceTest extends TestCase {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getContentPosition() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handleMessage(Message msg) {
|
||||
ExoPlayerMessage[] messages = (ExoPlayerMessage[]) msg.obj;
|
||||
|
@ -563,4 +563,11 @@ public interface ExoPlayer {
|
||||
*/
|
||||
int getCurrentAdIndexInAdGroup();
|
||||
|
||||
/**
|
||||
* If {@link #isPlayingAd()} returns {@code true}, returns the content position that will be
|
||||
* played once all ads in the ad group have finished playing, in milliseconds. If there is no ad
|
||||
* playing, the returned position is the same as that returned by {@link #getCurrentPosition()}.
|
||||
*/
|
||||
long getContentPosition();
|
||||
|
||||
}
|
||||
|
@ -367,6 +367,16 @@ import java.util.concurrent.CopyOnWriteArraySet;
|
||||
return pendingSeekAcks == 0 ? playbackInfo.periodId.adIndexInAdGroup : C.INDEX_UNSET;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getContentPosition() {
|
||||
if (isPlayingAd()) {
|
||||
timeline.getPeriod(playbackInfo.periodId.periodIndex, period);
|
||||
return period.getPositionInWindowMs() + C.usToMs(playbackInfo.contentPositionUs);
|
||||
} else {
|
||||
return getCurrentPosition();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRendererCount() {
|
||||
return renderers.length;
|
||||
|
@ -54,6 +54,7 @@ import java.io.IOException;
|
||||
|
||||
public final MediaPeriodId periodId;
|
||||
public final long startPositionUs;
|
||||
public final long contentPositionUs;
|
||||
|
||||
public volatile long positionUs;
|
||||
public volatile long bufferedPositionUs;
|
||||
@ -63,14 +64,20 @@ import java.io.IOException;
|
||||
}
|
||||
|
||||
public PlaybackInfo(MediaPeriodId periodId, long startPositionUs) {
|
||||
this(periodId, startPositionUs, C.TIME_UNSET);
|
||||
}
|
||||
|
||||
public PlaybackInfo(MediaPeriodId periodId, long startPositionUs, long contentPositionUs) {
|
||||
this.periodId = periodId;
|
||||
this.startPositionUs = startPositionUs;
|
||||
this.contentPositionUs = contentPositionUs;
|
||||
positionUs = startPositionUs;
|
||||
bufferedPositionUs = startPositionUs;
|
||||
}
|
||||
|
||||
public PlaybackInfo copyWithPeriodId(MediaPeriodId periodId) {
|
||||
PlaybackInfo playbackInfo = new PlaybackInfo(periodId.periodIndex, startPositionUs);
|
||||
public PlaybackInfo copyWithPeriodIndex(int periodIndex) {
|
||||
PlaybackInfo playbackInfo = new PlaybackInfo(periodId.copyWithPeriodIndex(periodIndex),
|
||||
startPositionUs, contentPositionUs);
|
||||
playbackInfo.positionUs = positionUs;
|
||||
playbackInfo.bufferedPositionUs = bufferedPositionUs;
|
||||
return playbackInfo;
|
||||
@ -486,7 +493,7 @@ import java.io.IOException;
|
||||
// position of the playing period to make sure none of the removed period is played.
|
||||
MediaPeriodId periodId = playingPeriodHolder.info.id;
|
||||
long newPositionUs = seekToPeriodPosition(periodId, playbackInfo.positionUs);
|
||||
playbackInfo = new PlaybackInfo(periodId, newPositionUs);
|
||||
playbackInfo = new PlaybackInfo(periodId, newPositionUs, playbackInfo.contentPositionUs);
|
||||
}
|
||||
}
|
||||
|
||||
@ -663,11 +670,11 @@ import java.io.IOException;
|
||||
boolean seekPositionAdjusted = seekPosition.windowPositionUs == C.TIME_UNSET;
|
||||
int periodIndex = periodPosition.first;
|
||||
long periodPositionUs = periodPosition.second;
|
||||
long contentPositionUs = periodPositionUs;
|
||||
MediaPeriodId periodId =
|
||||
mediaPeriodInfoSequence.resolvePeriodPositionForAds(periodIndex, periodPositionUs);
|
||||
if (periodId.isAd()) {
|
||||
seekPositionAdjusted = true;
|
||||
// TODO: Resume content at periodPositionUs after the ad plays.
|
||||
periodPositionUs = 0;
|
||||
}
|
||||
try {
|
||||
@ -680,7 +687,7 @@ import java.io.IOException;
|
||||
seekPositionAdjusted |= periodPositionUs != newPeriodPositionUs;
|
||||
periodPositionUs = newPeriodPositionUs;
|
||||
} finally {
|
||||
playbackInfo = new PlaybackInfo(periodId, periodPositionUs);
|
||||
playbackInfo = new PlaybackInfo(periodId, periodPositionUs, contentPositionUs);
|
||||
eventHandler.obtainMessage(MSG_SEEK_ACK, seekPositionAdjusted ? 1 : 0, 0, playbackInfo)
|
||||
.sendToTarget();
|
||||
}
|
||||
@ -985,16 +992,19 @@ import java.io.IOException;
|
||||
long positionUs = periodPosition.second;
|
||||
MediaPeriodId periodId =
|
||||
mediaPeriodInfoSequence.resolvePeriodPositionForAds(periodIndex, positionUs);
|
||||
playbackInfo = new PlaybackInfo(periodId, periodId.isAd() ? 0 : positionUs);
|
||||
playbackInfo = new PlaybackInfo(periodId, periodId.isAd() ? 0 : positionUs, positionUs);
|
||||
} else if (playbackInfo.startPositionUs == C.TIME_UNSET) {
|
||||
if (timeline.isEmpty()) {
|
||||
handleSourceInfoRefreshEndedPlayback(manifest, processedInitialSeekCount);
|
||||
return;
|
||||
}
|
||||
Pair<Integer, Long> defaultPosition = getPeriodPosition(0, C.TIME_UNSET);
|
||||
MediaPeriodId periodId = mediaPeriodInfoSequence.resolvePeriodPositionForAds(
|
||||
defaultPosition.first, defaultPosition.second);
|
||||
playbackInfo = new PlaybackInfo(periodId, periodId.isAd() ? 0 : defaultPosition.second);
|
||||
int periodIndex = defaultPosition.first;
|
||||
long startPositionUs = defaultPosition.second;
|
||||
MediaPeriodId periodId = mediaPeriodInfoSequence.resolvePeriodPositionForAds(periodIndex,
|
||||
startPositionUs);
|
||||
playbackInfo = new PlaybackInfo(periodId, periodId.isAd() ? 0 : startPositionUs,
|
||||
startPositionUs);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1047,8 +1057,7 @@ import java.io.IOException;
|
||||
// The current period is in the new timeline. Update the holder and playbackInfo.
|
||||
periodHolder = updatePeriodInfo(periodHolder, periodIndex);
|
||||
if (periodIndex != playbackInfo.periodId.periodIndex) {
|
||||
playbackInfo =
|
||||
playbackInfo.copyWithPeriodId(playbackInfo.periodId.copyWithPeriodIndex(periodIndex));
|
||||
playbackInfo = playbackInfo.copyWithPeriodIndex(periodIndex);
|
||||
}
|
||||
|
||||
// If there are subsequent holders, update the index for each of them. If we find a holder
|
||||
@ -1070,7 +1079,8 @@ import java.io.IOException;
|
||||
// position of the playing period to make sure none of the removed period is played.
|
||||
long newPositionUs =
|
||||
seekToPeriodPosition(playingPeriodHolder.info.id, playbackInfo.positionUs);
|
||||
playbackInfo = new PlaybackInfo(playingPeriodHolder.info.id, newPositionUs);
|
||||
playbackInfo = new PlaybackInfo(playingPeriodHolder.info.id, newPositionUs,
|
||||
playbackInfo.contentPositionUs);
|
||||
} else {
|
||||
// Update the loading period to be the last period that's still valid, and release all
|
||||
// subsequent periods.
|
||||
@ -1223,7 +1233,7 @@ import java.io.IOException;
|
||||
playingPeriodHolder.release();
|
||||
setPlayingPeriodHolder(playingPeriodHolder.next);
|
||||
playbackInfo = new PlaybackInfo(playingPeriodHolder.info.id,
|
||||
playingPeriodHolder.info.startPositionUs);
|
||||
playingPeriodHolder.info.startPositionUs, playingPeriodHolder.info.contentPositionUs);
|
||||
updatePlaybackPositions();
|
||||
eventHandler.obtainMessage(MSG_POSITION_DISCONTINUITY, playbackInfo).sendToTarget();
|
||||
}
|
||||
|
@ -47,6 +47,11 @@ import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId;
|
||||
* {@link C#TIME_END_OF_SOURCE} if the end position is the end of the media period.
|
||||
*/
|
||||
public final long endPositionUs;
|
||||
/**
|
||||
* If this is an ad, the position to play in the next content media period. {@link C#TIME_UNSET}
|
||||
* otherwise.
|
||||
*/
|
||||
public final long contentPositionUs;
|
||||
/**
|
||||
* The duration of the media to play within the media period, in microseconds, or
|
||||
* {@link C#TIME_UNSET} if not known.
|
||||
@ -64,10 +69,11 @@ import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId;
|
||||
public final boolean isFinal;
|
||||
|
||||
private MediaPeriodInfo(MediaPeriodId id, long startPositionUs, long endPositionUs,
|
||||
long durationUs, boolean isLastInTimelinePeriod, boolean isFinal) {
|
||||
long contentPositionUs, long durationUs, boolean isLastInTimelinePeriod, boolean isFinal) {
|
||||
this.id = id;
|
||||
this.startPositionUs = startPositionUs;
|
||||
this.endPositionUs = endPositionUs;
|
||||
this.contentPositionUs = contentPositionUs;
|
||||
this.durationUs = durationUs;
|
||||
this.isLastInTimelinePeriod = isLastInTimelinePeriod;
|
||||
this.isFinal = isFinal;
|
||||
@ -79,14 +85,14 @@ import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId;
|
||||
*/
|
||||
public MediaPeriodInfo copyWithPeriodIndex(int periodIndex) {
|
||||
return new MediaPeriodInfo(id.copyWithPeriodIndex(periodIndex), startPositionUs,
|
||||
endPositionUs, durationUs, isLastInTimelinePeriod, isFinal);
|
||||
endPositionUs, contentPositionUs, durationUs, isLastInTimelinePeriod, isFinal);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a copy of this instance with the start position set to the specified value.
|
||||
*/
|
||||
public MediaPeriodInfo copyWithStartPositionUs(long startPositionUs) {
|
||||
return new MediaPeriodInfo(id, startPositionUs, endPositionUs, durationUs,
|
||||
return new MediaPeriodInfo(id, startPositionUs, endPositionUs, contentPositionUs, durationUs,
|
||||
isLastInTimelinePeriod, isFinal);
|
||||
}
|
||||
|
||||
@ -127,7 +133,8 @@ import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId;
|
||||
* Returns the first {@link MediaPeriodInfo} to play, based on the specified playback position.
|
||||
*/
|
||||
public MediaPeriodInfo getFirstMediaPeriodInfo(PlaybackInfo playbackInfo) {
|
||||
return getMediaPeriodInfo(playbackInfo.periodId, playbackInfo.startPositionUs);
|
||||
return getMediaPeriodInfo(playbackInfo.periodId, playbackInfo.contentPositionUs,
|
||||
playbackInfo.startPositionUs);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -173,8 +180,8 @@ import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId;
|
||||
} else {
|
||||
startPositionUs = 0;
|
||||
}
|
||||
return getMediaPeriodInfo(resolvePeriodPositionForAds(nextPeriodIndex, startPositionUs),
|
||||
startPositionUs);
|
||||
MediaPeriodId periodId = resolvePeriodPositionForAds(nextPeriodIndex, startPositionUs);
|
||||
return getMediaPeriodInfo(periodId, startPositionUs, startPositionUs);
|
||||
}
|
||||
|
||||
MediaPeriodId currentPeriodId = currentMediaPeriodInfo.id;
|
||||
@ -190,18 +197,23 @@ import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId;
|
||||
// Play the next ad in the ad group if it's available.
|
||||
return !period.isAdAvailable(currentAdGroupIndex, nextAdIndexInAdGroup) ? null
|
||||
: getMediaPeriodInfoForAd(currentPeriodId.periodIndex, currentAdGroupIndex,
|
||||
nextAdIndexInAdGroup);
|
||||
nextAdIndexInAdGroup, currentMediaPeriodInfo.contentPositionUs);
|
||||
} else {
|
||||
// Play content from the ad group position.
|
||||
return getMediaPeriodInfo(new MediaPeriodId(currentPeriodId.periodIndex),
|
||||
period.getAdGroupTimeUs(currentAdGroupIndex));
|
||||
int nextAdGroupIndex =
|
||||
period.getAdGroupIndexAfterPositionUs(currentMediaPeriodInfo.contentPositionUs);
|
||||
long endUs = nextAdGroupIndex == C.INDEX_UNSET ? C.TIME_END_OF_SOURCE
|
||||
: period.getAdGroupTimeUs(nextAdGroupIndex);
|
||||
return getMediaPeriodInfoForContent(currentPeriodId.periodIndex,
|
||||
currentMediaPeriodInfo.contentPositionUs, endUs);
|
||||
}
|
||||
} else if (currentMediaPeriodInfo.endPositionUs != C.TIME_END_OF_SOURCE) {
|
||||
// Play the next ad group if it's available.
|
||||
int nextAdGroupIndex =
|
||||
period.getAdGroupIndexForPositionUs(currentMediaPeriodInfo.endPositionUs);
|
||||
return !period.isAdAvailable(nextAdGroupIndex, 0) ? null
|
||||
: getMediaPeriodInfoForAd(currentPeriodId.periodIndex, nextAdGroupIndex, 0);
|
||||
: getMediaPeriodInfoForAd(currentPeriodId.periodIndex, nextAdGroupIndex, 0,
|
||||
currentMediaPeriodInfo.endPositionUs);
|
||||
} else {
|
||||
// Check if the postroll ad should be played.
|
||||
int adGroupCount = period.getAdGroupCount();
|
||||
@ -211,7 +223,8 @@ import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId;
|
||||
|| !period.isAdAvailable(adGroupCount - 1, 0)) {
|
||||
return null;
|
||||
}
|
||||
return getMediaPeriodInfoForAd(currentPeriodId.periodIndex, adGroupCount - 1, 0);
|
||||
return getMediaPeriodInfoForAd(currentPeriodId.periodIndex, adGroupCount - 1, 0,
|
||||
currentMediaPeriodInfo.endPositionUs);
|
||||
}
|
||||
}
|
||||
|
||||
@ -223,8 +236,12 @@ import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId;
|
||||
public MediaPeriodId resolvePeriodPositionForAds(int periodIndex, long positionUs) {
|
||||
timeline.getPeriod(periodIndex, period);
|
||||
int adGroupIndex = period.getAdGroupIndexForPositionUs(positionUs);
|
||||
return adGroupIndex == C.INDEX_UNSET ? new MediaPeriodId(periodIndex)
|
||||
: new MediaPeriodId(periodIndex, adGroupIndex, 0);
|
||||
if (adGroupIndex == C.INDEX_UNSET) {
|
||||
return new MediaPeriodId(periodIndex);
|
||||
} else {
|
||||
int adIndexInAdGroup = period.getPlayedAdCount(adGroupIndex);
|
||||
return new MediaPeriodId(periodIndex, adGroupIndex, adIndexInAdGroup);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -255,17 +272,19 @@ import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId;
|
||||
long durationUs = newId.isAd()
|
||||
? period.getAdDurationUs(newId.adGroupIndex, newId.adIndexInAdGroup)
|
||||
: (endPositionUs == C.TIME_END_OF_SOURCE ? period.getDurationUs() : endPositionUs);
|
||||
return new MediaPeriodInfo(newId, startPositionUs, endPositionUs, durationUs, isLastInPeriod,
|
||||
isLastInTimeline);
|
||||
return new MediaPeriodInfo(newId, startPositionUs, endPositionUs, info.contentPositionUs,
|
||||
durationUs, isLastInPeriod, isLastInTimeline);
|
||||
}
|
||||
|
||||
private MediaPeriodInfo getMediaPeriodInfo(MediaPeriodId id, long startPositionUs) {
|
||||
private MediaPeriodInfo getMediaPeriodInfo(MediaPeriodId id, long contentPositionUs,
|
||||
long startPositionUs) {
|
||||
timeline.getPeriod(id.periodIndex, period);
|
||||
if (id.isAd()) {
|
||||
if (!period.isAdAvailable(id.adGroupIndex, id.adIndexInAdGroup)) {
|
||||
return null;
|
||||
}
|
||||
return getMediaPeriodInfoForAd(id.periodIndex, id.adGroupIndex, id.adIndexInAdGroup);
|
||||
return getMediaPeriodInfoForAd(id.periodIndex, id.adGroupIndex, id.adIndexInAdGroup,
|
||||
contentPositionUs);
|
||||
} else {
|
||||
int nextAdGroupIndex = period.getAdGroupIndexAfterPositionUs(startPositionUs);
|
||||
long endUs = nextAdGroupIndex == C.INDEX_UNSET ? C.TIME_END_OF_SOURCE
|
||||
@ -275,14 +294,14 @@ import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId;
|
||||
}
|
||||
|
||||
private MediaPeriodInfo getMediaPeriodInfoForAd(int periodIndex, int adGroupIndex,
|
||||
int adIndexInAdGroup) {
|
||||
int adIndexInAdGroup, long contentPositionUs) {
|
||||
MediaPeriodId id = new MediaPeriodId(periodIndex, adGroupIndex, adIndexInAdGroup);
|
||||
boolean isLastInPeriod = isLastInPeriod(id, C.TIME_END_OF_SOURCE);
|
||||
boolean isLastInTimeline = isLastInTimeline(id, isLastInPeriod);
|
||||
long durationUs = timeline.getPeriod(id.periodIndex, period)
|
||||
.getAdDurationUs(id.adGroupIndex, id.adIndexInAdGroup);
|
||||
return new MediaPeriodInfo(id, 0, C.TIME_END_OF_SOURCE, durationUs, isLastInPeriod,
|
||||
isLastInTimeline);
|
||||
return new MediaPeriodInfo(id, 0, C.TIME_END_OF_SOURCE, contentPositionUs, durationUs,
|
||||
isLastInPeriod, isLastInTimeline);
|
||||
}
|
||||
|
||||
private MediaPeriodInfo getMediaPeriodInfoForContent(int periodIndex, long startPositionUs,
|
||||
@ -292,7 +311,7 @@ import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId;
|
||||
boolean isLastInTimeline = isLastInTimeline(id, isLastInPeriod);
|
||||
timeline.getPeriod(id.periodIndex, period);
|
||||
long durationUs = endUs == C.TIME_END_OF_SOURCE ? period.getDurationUs() : endUs;
|
||||
return new MediaPeriodInfo(id, startPositionUs, endUs, durationUs, isLastInPeriod,
|
||||
return new MediaPeriodInfo(id, startPositionUs, endUs, C.TIME_UNSET, durationUs, isLastInPeriod,
|
||||
isLastInTimeline);
|
||||
}
|
||||
|
||||
|
@ -715,6 +715,11 @@ public class SimpleExoPlayer implements ExoPlayer {
|
||||
return player.getCurrentAdIndexInAdGroup();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getContentPosition() {
|
||||
return player.getContentPosition();
|
||||
}
|
||||
|
||||
// Internal methods.
|
||||
|
||||
private void removeSurfaceCallbacks() {
|
||||
|
@ -378,6 +378,16 @@ public abstract class Timeline {
|
||||
return adGroupTimesUs[adGroupIndex];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of ads that have been played in the specified ad group in the period.
|
||||
*
|
||||
* @param adGroupIndex The ad group index.
|
||||
* @return The number of ads that have been played.
|
||||
*/
|
||||
public int getPlayedAdCount(int adGroupIndex) {
|
||||
return adsPlayedCounts[adGroupIndex];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the ad group at index {@code adGroupIndex} has been played.
|
||||
*
|
||||
|
Loading…
x
Reference in New Issue
Block a user