Commit c1181000 by tonihei Committed by Oliver Woodman

Correctly report buffered position for multi-period window.

Currently only the buffered position in the current media period can be queried.

To achieve this, we save the buffered positions of all MediaPeriods to the
PlaybackInfo together with a list of MediaPeriodIds. ExoPlayerImpl can then
determine the correct buffered position for multi-period windows and windows
with midroll ads.

In addition, this change adds two new convenience methods to the Player interface
to query the total buffered duration across all windows and to get the buffered
duration of the content while playing an ad.

Issue:#4023

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=200041791
parent d6878f15
...@@ -21,6 +21,10 @@ ...@@ -21,6 +21,10 @@
* Add method to `BandwidthMeter` to return the `TransferListener` used to gather * Add method to `BandwidthMeter` to return the `TransferListener` used to gather
bandwidth information. bandwidth information.
* Add callback to `VideoListener` to notify of surface size changes. * Add callback to `VideoListener` to notify of surface size changes.
* Fix bug when reporting buffered position for multi-period windows and add
two additional convenience methods `Player.getTotalBufferedDuration` and
`Player.getContentBufferedDuration`
([#4023](https://github.com/google/ExoPlayer/issues/4023)).
* Allow apps to register custom MIME types * Allow apps to register custom MIME types
([#4264](https://github.com/google/ExoPlayer/issues/4264)). ([#4264](https://github.com/google/ExoPlayer/issues/4264)).
......
...@@ -527,6 +527,15 @@ public final class CastPlayer implements Player { ...@@ -527,6 +527,15 @@ public final class CastPlayer implements Player {
} }
@Override @Override
public long getTotalBufferedDuration() {
long bufferedPosition = getBufferedPosition();
long currentPosition = getCurrentPosition();
return bufferedPosition == C.TIME_UNSET || currentPosition == C.TIME_UNSET
? 0
: bufferedPosition - currentPosition;
}
@Override
public boolean isCurrentWindowDynamic() { public boolean isCurrentWindowDynamic() {
return !currentTimeline.isEmpty() return !currentTimeline.isEmpty()
&& currentTimeline.getWindow(getCurrentWindowIndex(), window).isDynamic; && currentTimeline.getWindow(getCurrentWindowIndex(), window).isDynamic;
...@@ -563,6 +572,11 @@ public final class CastPlayer implements Player { ...@@ -563,6 +572,11 @@ public final class CastPlayer implements Player {
return getCurrentPosition(); return getCurrentPosition();
} }
@Override
public long getContentBufferedPosition() {
return getBufferedPosition();
}
// Internal methods. // Internal methods.
public void updateInternalState() { public void updateInternalState() {
......
...@@ -470,30 +470,38 @@ import java.util.concurrent.CopyOnWriteArraySet; ...@@ -470,30 +470,38 @@ import java.util.concurrent.CopyOnWriteArraySet;
public long getCurrentPosition() { public long getCurrentPosition() {
if (shouldMaskPosition()) { if (shouldMaskPosition()) {
return maskingWindowPositionMs; return maskingWindowPositionMs;
} else if (playbackInfo.periodId.isAd()) {
return C.usToMs(playbackInfo.positionUs);
} else { } else {
return playbackInfoPositionUsToWindowPositionMs(playbackInfo.positionUs); return periodPositionUsToWindowPositionMs(playbackInfo.periodId, playbackInfo.positionUs);
} }
} }
@Override @Override
public long getBufferedPosition() { public long getBufferedPosition() {
// TODO - Implement this properly. if (isPlayingAd()) {
if (shouldMaskPosition()) { return playbackInfo.loadingMediaPeriodId.equals(playbackInfo.periodId)
return maskingWindowPositionMs; ? C.usToMs(playbackInfo.bufferedPositionUs)
} else { : getDuration();
return playbackInfoPositionUsToWindowPositionMs(playbackInfo.bufferedPositionUs);
} }
return getContentBufferedPosition();
} }
@Override @Override
public int getBufferedPercentage() { public int getBufferedPercentage() {
long position = getBufferedPosition(); long position = getBufferedPosition();
long duration = getDuration(); long duration = getDuration();
return position == C.TIME_UNSET || duration == C.TIME_UNSET ? 0 return position == C.TIME_UNSET || duration == C.TIME_UNSET
? 0
: (duration == 0 ? 100 : Util.constrainValue((int) ((position * 100) / duration), 0, 100)); : (duration == 0 ? 100 : Util.constrainValue((int) ((position * 100) / duration), 0, 100));
} }
@Override @Override
public long getTotalBufferedDuration() {
return Math.max(0, C.usToMs(playbackInfo.totalBufferedDurationUs));
}
@Override
public boolean isCurrentWindowDynamic() { public boolean isCurrentWindowDynamic() {
Timeline timeline = playbackInfo.timeline; Timeline timeline = playbackInfo.timeline;
return !timeline.isEmpty() && timeline.getWindow(getCurrentWindowIndex(), window).isDynamic; return !timeline.isEmpty() && timeline.getWindow(getCurrentWindowIndex(), window).isDynamic;
...@@ -531,6 +539,29 @@ import java.util.concurrent.CopyOnWriteArraySet; ...@@ -531,6 +539,29 @@ import java.util.concurrent.CopyOnWriteArraySet;
} }
@Override @Override
public long getContentBufferedPosition() {
if (shouldMaskPosition()) {
return maskingWindowPositionMs;
}
if (playbackInfo.loadingMediaPeriodId.windowSequenceNumber
!= playbackInfo.periodId.windowSequenceNumber) {
return playbackInfo.timeline.getWindow(getCurrentWindowIndex(), window).getDurationMs();
}
long contentBufferedPositionUs = playbackInfo.bufferedPositionUs;
if (playbackInfo.loadingMediaPeriodId.isAd()) {
Timeline.Period loadingPeriod =
playbackInfo.timeline.getPeriod(playbackInfo.loadingMediaPeriodId.periodIndex, period);
contentBufferedPositionUs =
loadingPeriod.getAdGroupTimeUs(playbackInfo.loadingMediaPeriodId.adGroupIndex);
if (contentBufferedPositionUs == C.TIME_END_OF_SOURCE) {
contentBufferedPositionUs = loadingPeriod.durationUs;
}
}
return periodPositionUsToWindowPositionMs(
playbackInfo.loadingMediaPeriodId, contentBufferedPositionUs);
}
@Override
public int getRendererCount() { public int getRendererCount() {
return renderers.length; return renderers.length;
} }
...@@ -649,7 +680,11 @@ import java.util.concurrent.CopyOnWriteArraySet; ...@@ -649,7 +680,11 @@ import java.util.concurrent.CopyOnWriteArraySet;
playbackState, playbackState,
/* isLoading= */ false, /* isLoading= */ false,
resetState ? TrackGroupArray.EMPTY : playbackInfo.trackGroups, resetState ? TrackGroupArray.EMPTY : playbackInfo.trackGroups,
resetState ? emptyTrackSelectorResult : playbackInfo.trackSelectorResult); resetState ? emptyTrackSelectorResult : playbackInfo.trackSelectorResult,
playbackInfo.periodId,
playbackInfo.startPositionUs,
/* totalBufferedDurationUs= */ 0,
playbackInfo.startPositionUs);
} }
private void updatePlaybackInfo( private void updatePlaybackInfo(
...@@ -683,12 +718,10 @@ import java.util.concurrent.CopyOnWriteArraySet; ...@@ -683,12 +718,10 @@ import java.util.concurrent.CopyOnWriteArraySet;
} }
} }
private long playbackInfoPositionUsToWindowPositionMs(long positionUs) { private long periodPositionUsToWindowPositionMs(MediaPeriodId periodId, long positionUs) {
long positionMs = C.usToMs(positionUs); long positionMs = C.usToMs(positionUs);
if (!playbackInfo.periodId.isAd()) { playbackInfo.timeline.getPeriod(periodId.periodIndex, period);
playbackInfo.timeline.getPeriod(playbackInfo.periodId.periodIndex, period); positionMs += period.getPositionInWindowMs();
positionMs += period.getPositionInWindowMs();
}
return positionMs; return positionMs;
} }
......
...@@ -419,6 +419,7 @@ import java.util.Collections; ...@@ -419,6 +419,7 @@ import java.util.Collections;
if (!queue.updateRepeatMode(repeatMode)) { if (!queue.updateRepeatMode(repeatMode)) {
seekToCurrentPosition(/* sendDiscontinuity= */ true); seekToCurrentPosition(/* sendDiscontinuity= */ true);
} }
updateLoadingMediaPeriodId();
} }
private void setShuffleModeEnabledInternal(boolean shuffleModeEnabled) private void setShuffleModeEnabledInternal(boolean shuffleModeEnabled)
...@@ -427,6 +428,7 @@ import java.util.Collections; ...@@ -427,6 +428,7 @@ import java.util.Collections;
if (!queue.updateShuffleModeEnabled(shuffleModeEnabled)) { if (!queue.updateShuffleModeEnabled(shuffleModeEnabled)) {
seekToCurrentPosition(/* sendDiscontinuity= */ true); seekToCurrentPosition(/* sendDiscontinuity= */ true);
} }
updateLoadingMediaPeriodId();
} }
private void seekToCurrentPosition(boolean sendDiscontinuity) throws ExoPlaybackException { private void seekToCurrentPosition(boolean sendDiscontinuity) throws ExoPlaybackException {
...@@ -483,11 +485,12 @@ import java.util.Collections; ...@@ -483,11 +485,12 @@ import java.util.Collections;
playbackInfo.positionUs = periodPositionUs; playbackInfo.positionUs = periodPositionUs;
} }
// Update the buffered position. // Update the buffered position and total buffered duration.
MediaPeriodHolder loadingPeriod = queue.getLoadingPeriod();
playbackInfo.bufferedPositionUs = playbackInfo.bufferedPositionUs =
enabledRenderers.length == 0 loadingPeriod.getBufferedPositionUs(/* convertEosToDuration= */ true);
? playingPeriodHolder.info.durationUs playbackInfo.totalBufferedDurationUs =
: playingPeriodHolder.getBufferedPositionUs(/* convertEosToDuration= */ true); playbackInfo.bufferedPositionUs - loadingPeriod.toPeriodTime(rendererPositionUs);
} }
private void doSomeWork() throws ExoPlaybackException, IOException { private void doSomeWork() throws ExoPlaybackException, IOException {
...@@ -691,6 +694,7 @@ import java.util.Collections; ...@@ -691,6 +694,7 @@ import java.util.Collections;
resetRendererPosition(periodPositionUs); resetRendererPosition(periodPositionUs);
} }
updateLoadingMediaPeriodId();
handler.sendEmptyMessage(MSG_DO_SOME_WORK); handler.sendEmptyMessage(MSG_DO_SOME_WORK);
return periodPositionUs; return periodPositionUs;
} }
...@@ -785,18 +789,26 @@ import java.util.Collections; ...@@ -785,18 +789,26 @@ import java.util.Collections;
pendingMessages.clear(); pendingMessages.clear();
nextPendingMessageIndex = 0; nextPendingMessageIndex = 0;
} }
// Set the start position to TIME_UNSET so that a subsequent seek to 0 isn't ignored.
MediaPeriodId mediaPeriodId =
resetPosition ? new MediaPeriodId(getFirstPeriodIndex()) : playbackInfo.periodId;
long startPositionUs = resetPosition ? C.TIME_UNSET : playbackInfo.positionUs;
long contentPositionUs = resetPosition ? C.TIME_UNSET : playbackInfo.contentPositionUs;
playbackInfo = playbackInfo =
new PlaybackInfo( new PlaybackInfo(
resetState ? Timeline.EMPTY : playbackInfo.timeline, resetState ? Timeline.EMPTY : playbackInfo.timeline,
resetState ? null : playbackInfo.manifest, resetState ? null : playbackInfo.manifest,
resetPosition ? new MediaPeriodId(getFirstPeriodIndex()) : playbackInfo.periodId, mediaPeriodId,
// Set the start position to TIME_UNSET so that a subsequent seek to 0 isn't ignored. startPositionUs,
resetPosition ? C.TIME_UNSET : playbackInfo.positionUs, contentPositionUs,
resetPosition ? C.TIME_UNSET : playbackInfo.contentPositionUs,
playbackInfo.playbackState, playbackInfo.playbackState,
/* isLoading= */ false, /* isLoading= */ false,
resetState ? TrackGroupArray.EMPTY : playbackInfo.trackGroups, resetState ? TrackGroupArray.EMPTY : playbackInfo.trackGroups,
resetState ? emptyTrackSelectorResult : playbackInfo.trackSelectorResult); resetState ? emptyTrackSelectorResult : playbackInfo.trackSelectorResult,
mediaPeriodId,
startPositionUs,
/* totalBufferedDurationUs= */ 0,
startPositionUs);
if (releaseMediaSource) { if (releaseMediaSource) {
if (mediaSource != null) { if (mediaSource != null) {
mediaSource.releaseSource(/* listener= */ this); mediaSource.releaseSource(/* listener= */ this);
...@@ -1051,6 +1063,7 @@ import java.util.Collections; ...@@ -1051,6 +1063,7 @@ import java.util.Collections;
updateLoadControlTrackSelection(periodHolder.trackGroups, periodHolder.trackSelectorResult); updateLoadControlTrackSelection(periodHolder.trackGroups, periodHolder.trackSelectorResult);
} }
} }
updateLoadingMediaPeriodId();
if (playbackInfo.playbackState != Player.STATE_ENDED) { if (playbackInfo.playbackState != Player.STATE_ENDED) {
maybeContinueLoading(); maybeContinueLoading();
updatePlaybackPositions(); updatePlaybackPositions();
...@@ -1249,6 +1262,7 @@ import java.util.Collections; ...@@ -1249,6 +1262,7 @@ import java.util.Collections;
if (!queue.updateQueuedPeriods(playingPeriodId, rendererPositionUs)) { if (!queue.updateQueuedPeriods(playingPeriodId, rendererPositionUs)) {
seekToCurrentPosition(/* sendDiscontinuity= */ false); seekToCurrentPosition(/* sendDiscontinuity= */ false);
} }
updateLoadingMediaPeriodId();
} }
private void handleSourceInfoRefreshEndedPlayback() { private void handleSourceInfoRefreshEndedPlayback() {
...@@ -1494,6 +1508,7 @@ import java.util.Collections; ...@@ -1494,6 +1508,7 @@ import java.util.Collections;
info); info);
mediaPeriod.prepare(this, info.startPositionUs); mediaPeriod.prepare(this, info.startPositionUs);
setIsLoading(true); setIsLoading(true);
updateLoadingMediaPeriodId();
} }
} }
} }
...@@ -1620,6 +1635,13 @@ import java.util.Collections; ...@@ -1620,6 +1635,13 @@ import java.util.Collections;
&& renderer.hasReadStreamToEnd(); && renderer.hasReadStreamToEnd();
} }
private void updateLoadingMediaPeriodId() {
MediaPeriodHolder loadingMediaPeriodHolder = queue.getLoadingPeriod();
MediaPeriodId loadingMediaPeriodId =
loadingMediaPeriodHolder == null ? playbackInfo.periodId : loadingMediaPeriodHolder.info.id;
playbackInfo = playbackInfo.copyWithLoadingMediaPeriodId(loadingMediaPeriodId);
}
@NonNull @NonNull
private static Format[] getFormats(TrackSelection newSelection) { private static Format[] getFormats(TrackSelection newSelection) {
// Build an array of formats contained by the selection. // Build an array of formats contained by the selection.
......
...@@ -127,7 +127,8 @@ import com.google.android.exoplayer2.util.Assertions; ...@@ -127,7 +127,8 @@ import com.google.android.exoplayer2.util.Assertions;
if (!prepared) { if (!prepared) {
return info.startPositionUs; return info.startPositionUs;
} }
long bufferedPositionUs = mediaPeriod.getBufferedPositionUs(); long bufferedPositionUs =
hasEnabledTracks ? mediaPeriod.getBufferedPositionUs() : C.TIME_END_OF_SOURCE;
return bufferedPositionUs == C.TIME_END_OF_SOURCE && convertEosToDuration return bufferedPositionUs == C.TIME_END_OF_SOURCE && convertEosToDuration
? info.durationUs ? info.durationUs
: bufferedPositionUs; : bufferedPositionUs;
......
...@@ -25,18 +25,49 @@ import com.google.android.exoplayer2.trackselection.TrackSelectorResult; ...@@ -25,18 +25,49 @@ import com.google.android.exoplayer2.trackselection.TrackSelectorResult;
*/ */
/* package */ final class PlaybackInfo { /* package */ final class PlaybackInfo {
/** The current {@link Timeline}. */
public final Timeline timeline; public final Timeline timeline;
/** The current manifest. */
public final @Nullable Object manifest; public final @Nullable Object manifest;
/** The {@link MediaPeriodId} of the currently playing media period in the {@link #timeline}. */
public final MediaPeriodId periodId; public final MediaPeriodId periodId;
/**
* The start position at which playback started in {@link #periodId} relative to the start of the
* associated period in the {@link #timeline}, in microseconds.
*/
public final long startPositionUs; public final long startPositionUs;
/**
* If {@link #periodId} refers to an ad, the position of the suspended content relative to the
* start of the associated period in the {@link #timeline}, in microseconds. {@link C#TIME_UNSET}
* if {@link #periodId} does not refer to an ad.
*/
public final long contentPositionUs; public final long contentPositionUs;
/** The current playback state. One of the {@link Player}.STATE_ constants. */
public final int playbackState; public final int playbackState;
/** Whether the player is currently loading. */
public final boolean isLoading; public final boolean isLoading;
/** The currently available track groups. */
public final TrackGroupArray trackGroups; public final TrackGroupArray trackGroups;
/** The result of the current track selection. */
public final TrackSelectorResult trackSelectorResult; public final TrackSelectorResult trackSelectorResult;
/** The {@link MediaPeriodId} of the currently loading media period in the {@link #timeline}. */
public final MediaPeriodId loadingMediaPeriodId;
public volatile long positionUs; /**
* Position up to which media is buffered in {@link #loadingMediaPeriodId) relative to the start
* of the associated period in the {@link #timeline}, in microseconds.
*/
public volatile long bufferedPositionUs; public volatile long bufferedPositionUs;
/**
* Total duration of buffered media from {@link #positionUs} to {@link #bufferedPositionUs}
* including all ads.
*/
public volatile long totalBufferedDurationUs;
/**
* Current playback position in {@link #periodId} relative to the start of the associated period
* in the {@link #timeline}, in microseconds.
*/
public volatile long positionUs;
public PlaybackInfo( public PlaybackInfo(
Timeline timeline, Timeline timeline,
...@@ -52,7 +83,11 @@ import com.google.android.exoplayer2.trackselection.TrackSelectorResult; ...@@ -52,7 +83,11 @@ import com.google.android.exoplayer2.trackselection.TrackSelectorResult;
Player.STATE_IDLE, Player.STATE_IDLE,
/* isLoading= */ false, /* isLoading= */ false,
trackGroups, trackGroups,
trackSelectorResult); trackSelectorResult,
new MediaPeriodId(/* periodIndex= */ 0),
startPositionUs,
/* totalBufferedDurationUs= */ 0,
startPositionUs);
} }
public PlaybackInfo( public PlaybackInfo(
...@@ -64,18 +99,24 @@ import com.google.android.exoplayer2.trackselection.TrackSelectorResult; ...@@ -64,18 +99,24 @@ import com.google.android.exoplayer2.trackselection.TrackSelectorResult;
int playbackState, int playbackState,
boolean isLoading, boolean isLoading,
TrackGroupArray trackGroups, TrackGroupArray trackGroups,
TrackSelectorResult trackSelectorResult) { TrackSelectorResult trackSelectorResult,
MediaPeriodId loadingMediaPeriodId,
long bufferedPositionUs,
long totalBufferedDurationUs,
long positionUs) {
this.timeline = timeline; this.timeline = timeline;
this.manifest = manifest; this.manifest = manifest;
this.periodId = periodId; this.periodId = periodId;
this.startPositionUs = startPositionUs; this.startPositionUs = startPositionUs;
this.contentPositionUs = contentPositionUs; this.contentPositionUs = contentPositionUs;
this.positionUs = startPositionUs;
this.bufferedPositionUs = startPositionUs;
this.playbackState = playbackState; this.playbackState = playbackState;
this.isLoading = isLoading; this.isLoading = isLoading;
this.trackGroups = trackGroups; this.trackGroups = trackGroups;
this.trackSelectorResult = trackSelectorResult; this.trackSelectorResult = trackSelectorResult;
this.loadingMediaPeriodId = loadingMediaPeriodId;
this.bufferedPositionUs = bufferedPositionUs;
this.totalBufferedDurationUs = totalBufferedDurationUs;
this.positionUs = positionUs;
} }
public PlaybackInfo fromNewPosition( public PlaybackInfo fromNewPosition(
...@@ -89,93 +130,113 @@ import com.google.android.exoplayer2.trackselection.TrackSelectorResult; ...@@ -89,93 +130,113 @@ import com.google.android.exoplayer2.trackselection.TrackSelectorResult;
playbackState, playbackState,
isLoading, isLoading,
trackGroups, trackGroups,
trackSelectorResult); trackSelectorResult,
periodId,
startPositionUs,
/* totalBufferedDurationUs= */ 0,
startPositionUs);
} }
public PlaybackInfo copyWithPeriodIndex(int periodIndex) { public PlaybackInfo copyWithPeriodIndex(int periodIndex) {
PlaybackInfo playbackInfo = return new PlaybackInfo(
new PlaybackInfo( timeline,
timeline, manifest,
manifest, periodId.copyWithPeriodIndex(periodIndex),
periodId.copyWithPeriodIndex(periodIndex), startPositionUs,
startPositionUs, contentPositionUs,
contentPositionUs, playbackState,
playbackState, isLoading,
isLoading, trackGroups,
trackGroups, trackSelectorResult,
trackSelectorResult); loadingMediaPeriodId,
copyMutablePositions(this, playbackInfo); bufferedPositionUs,
return playbackInfo; totalBufferedDurationUs,
positionUs);
} }
public PlaybackInfo copyWithTimeline(Timeline timeline, Object manifest) { public PlaybackInfo copyWithTimeline(Timeline timeline, Object manifest) {
PlaybackInfo playbackInfo = return new PlaybackInfo(
new PlaybackInfo( timeline,
timeline, manifest,
manifest, periodId,
periodId, startPositionUs,
startPositionUs, contentPositionUs,
contentPositionUs, playbackState,
playbackState, isLoading,
isLoading, trackGroups,
trackGroups, trackSelectorResult,
trackSelectorResult); loadingMediaPeriodId,
copyMutablePositions(this, playbackInfo); bufferedPositionUs,
return playbackInfo; totalBufferedDurationUs,
positionUs);
} }
public PlaybackInfo copyWithPlaybackState(int playbackState) { public PlaybackInfo copyWithPlaybackState(int playbackState) {
PlaybackInfo playbackInfo = return new PlaybackInfo(
new PlaybackInfo( timeline,
timeline, manifest,
manifest, periodId,
periodId, startPositionUs,
startPositionUs, contentPositionUs,
contentPositionUs, playbackState,
playbackState, isLoading,
isLoading, trackGroups,
trackGroups, trackSelectorResult,
trackSelectorResult); loadingMediaPeriodId,
copyMutablePositions(this, playbackInfo); bufferedPositionUs,
return playbackInfo; totalBufferedDurationUs,
positionUs);
} }
public PlaybackInfo copyWithIsLoading(boolean isLoading) { public PlaybackInfo copyWithIsLoading(boolean isLoading) {
PlaybackInfo playbackInfo = return new PlaybackInfo(
new PlaybackInfo( timeline,
timeline, manifest,
manifest, periodId,
periodId, startPositionUs,
startPositionUs, contentPositionUs,
contentPositionUs, playbackState,
playbackState, isLoading,
isLoading, trackGroups,
trackGroups, trackSelectorResult,
trackSelectorResult); loadingMediaPeriodId,
copyMutablePositions(this, playbackInfo); bufferedPositionUs,
return playbackInfo; totalBufferedDurationUs,
positionUs);
} }
public PlaybackInfo copyWithTrackInfo( public PlaybackInfo copyWithTrackInfo(
TrackGroupArray trackGroups, TrackSelectorResult trackSelectorResult) { TrackGroupArray trackGroups, TrackSelectorResult trackSelectorResult) {
PlaybackInfo playbackInfo = return new PlaybackInfo(
new PlaybackInfo( timeline,
timeline, manifest,
manifest, periodId,
periodId, startPositionUs,
startPositionUs, contentPositionUs,
contentPositionUs, playbackState,
playbackState, isLoading,
isLoading, trackGroups,
trackGroups, trackSelectorResult,
trackSelectorResult); loadingMediaPeriodId,
copyMutablePositions(this, playbackInfo); bufferedPositionUs,
return playbackInfo; totalBufferedDurationUs,
positionUs);
} }
private static void copyMutablePositions(PlaybackInfo from, PlaybackInfo to) { public PlaybackInfo copyWithLoadingMediaPeriodId(MediaPeriodId loadingMediaPeriodId) {
to.positionUs = from.positionUs; return new PlaybackInfo(
to.bufferedPositionUs = from.bufferedPositionUs; timeline,
manifest,
periodId,
startPositionUs,
contentPositionUs,
playbackState,
isLoading,
trackGroups,
trackSelectorResult,
loadingMediaPeriodId,
bufferedPositionUs,
totalBufferedDurationUs,
positionUs);
} }
} }
...@@ -678,24 +678,28 @@ public interface Player { ...@@ -678,24 +678,28 @@ public interface Player {
*/ */
long getDuration(); long getDuration();
/** /** Returns the playback position in the current content window or ad, in milliseconds. */
* Returns the playback position in the current window, in milliseconds.
*/
long getCurrentPosition(); long getCurrentPosition();
/** /**
* Returns an estimate of the position in the current window up to which data is buffered, in * Returns an estimate of the position in the current content window or ad up to which data is
* milliseconds. * buffered, in milliseconds.
*/ */
long getBufferedPosition(); long getBufferedPosition();
/** /**
* Returns an estimate of the percentage in the current window up to which data is buffered, or 0 * Returns an estimate of the percentage in the current content window or ad up to which data is
* if no estimate is available. * buffered, or 0 if no estimate is available.
*/ */
int getBufferedPercentage(); int getBufferedPercentage();
/** /**
* Returns an estimate of the total buffered duration from the current position, in milliseconds.
* This includes pre-buffered data for subsequent ads and windows.
*/
long getTotalBufferedDuration();
/**
* Returns whether the current window is dynamic, or {@code false} if the {@link Timeline} is * Returns whether the current window is dynamic, or {@code false} if the {@link Timeline} is
* empty. * empty.
* *
...@@ -735,4 +739,10 @@ public interface Player { ...@@ -735,4 +739,10 @@ public interface Player {
*/ */
long getContentPosition(); long getContentPosition();
/**
* If {@link #isPlayingAd()} returns {@code true}, returns an estimate of the content position in
* the current content window up to which data is buffered, in milliseconds. If there is no ad
* playing, the returned position is the same as that returned by {@link #getBufferedPosition()}.
*/
long getContentBufferedPosition();
} }
...@@ -906,6 +906,11 @@ public class SimpleExoPlayer implements ExoPlayer, Player.VideoComponent, Player ...@@ -906,6 +906,11 @@ public class SimpleExoPlayer implements ExoPlayer, Player.VideoComponent, Player
} }
@Override @Override
public long getTotalBufferedDuration() {
return player.getTotalBufferedDuration();
}
@Override
public boolean isCurrentWindowDynamic() { public boolean isCurrentWindowDynamic() {
return player.isCurrentWindowDynamic(); return player.isCurrentWindowDynamic();
} }
...@@ -935,6 +940,11 @@ public class SimpleExoPlayer implements ExoPlayer, Player.VideoComponent, Player ...@@ -935,6 +940,11 @@ public class SimpleExoPlayer implements ExoPlayer, Player.VideoComponent, Player
return player.getContentPosition(); return player.getContentPosition();
} }
@Override
public long getContentBufferedPosition() {
return player.getContentBufferedPosition();
}
// Internal methods. // Internal methods.
/** /**
......
...@@ -715,7 +715,7 @@ public class PlayerControlView extends FrameLayout { ...@@ -715,7 +715,7 @@ public class PlayerControlView extends FrameLayout {
long bufferedPosition = 0; long bufferedPosition = 0;
long duration = 0; long duration = 0;
if (player != null) { if (player != null) {
long currentWindowTimeBarOffsetUs = 0; long currentWindowTimeBarOffsetMs = 0;
long durationUs = 0; long durationUs = 0;
int adGroupCount = 0; int adGroupCount = 0;
Timeline timeline = player.getCurrentTimeline(); Timeline timeline = player.getCurrentTimeline();
...@@ -726,7 +726,7 @@ public class PlayerControlView extends FrameLayout { ...@@ -726,7 +726,7 @@ public class PlayerControlView extends FrameLayout {
multiWindowTimeBar ? timeline.getWindowCount() - 1 : currentWindowIndex; multiWindowTimeBar ? timeline.getWindowCount() - 1 : currentWindowIndex;
for (int i = firstWindowIndex; i <= lastWindowIndex; i++) { for (int i = firstWindowIndex; i <= lastWindowIndex; i++) {
if (i == currentWindowIndex) { if (i == currentWindowIndex) {
currentWindowTimeBarOffsetUs = durationUs; currentWindowTimeBarOffsetMs = C.usToMs(durationUs);
} }
timeline.getWindow(i, window); timeline.getWindow(i, window);
if (window.durationUs == C.TIME_UNSET) { if (window.durationUs == C.TIME_UNSET) {
...@@ -762,15 +762,8 @@ public class PlayerControlView extends FrameLayout { ...@@ -762,15 +762,8 @@ public class PlayerControlView extends FrameLayout {
} }
} }
duration = C.usToMs(durationUs); duration = C.usToMs(durationUs);
position = C.usToMs(currentWindowTimeBarOffsetUs); position = currentWindowTimeBarOffsetMs + player.getContentPosition();
bufferedPosition = position; bufferedPosition = currentWindowTimeBarOffsetMs + player.getContentBufferedPosition();
if (player.isPlayingAd()) {
position += player.getContentPosition();
bufferedPosition = position;
} else {
position += player.getCurrentPosition();
bufferedPosition += player.getBufferedPosition();
}
if (timeBar != null) { if (timeBar != null) {
int extraAdGroupCount = extraAdGroupTimesMs.length; int extraAdGroupCount = extraAdGroupTimesMs.length;
int totalAdGroupCount = adGroupCount + extraAdGroupCount; int totalAdGroupCount = adGroupCount + extraAdGroupCount;
......
...@@ -255,6 +255,11 @@ public abstract class StubExoPlayer implements ExoPlayer { ...@@ -255,6 +255,11 @@ public abstract class StubExoPlayer implements ExoPlayer {
} }
@Override @Override
public long getTotalBufferedDuration() {
throw new UnsupportedOperationException();
}
@Override
public boolean isCurrentWindowDynamic() { public boolean isCurrentWindowDynamic() {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
...@@ -283,4 +288,9 @@ public abstract class StubExoPlayer implements ExoPlayer { ...@@ -283,4 +288,9 @@ public abstract class StubExoPlayer implements ExoPlayer {
public long getContentPosition() { public long getContentPosition() {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
@Override
public long getContentBufferedPosition() {
throw new UnsupportedOperationException();
}
} }
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