mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08:00
Add basic multi-player support to DefaultLoadControl
This change makes sure the `DefaultLoadControl` would work when passed to multiple players. It makes sure and unit tests that the loading state of a player is maintained for each player that is using `DefaultLoadControl`. The targetBufferSize of the `DefaultAllocator` is increased linearly for each player and memory is allocated in a simple first-come-first-serve manner. PiperOrigin-RevId: 622126523
This commit is contained in:
parent
e0fa697edf
commit
08cc6e673d
@ -15,23 +15,27 @@
|
|||||||
*/
|
*/
|
||||||
package androidx.media3.exoplayer;
|
package androidx.media3.exoplayer;
|
||||||
|
|
||||||
|
import static androidx.media3.common.util.Assertions.checkNotNull;
|
||||||
import static androidx.media3.common.util.Assertions.checkState;
|
import static androidx.media3.common.util.Assertions.checkState;
|
||||||
import static java.lang.Math.max;
|
import static java.lang.Math.max;
|
||||||
import static java.lang.Math.min;
|
import static java.lang.Math.min;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.annotation.VisibleForTesting;
|
||||||
import androidx.media3.common.C;
|
import androidx.media3.common.C;
|
||||||
import androidx.media3.common.Timeline;
|
import androidx.media3.common.Timeline;
|
||||||
import androidx.media3.common.util.Assertions;
|
import androidx.media3.common.util.Assertions;
|
||||||
import androidx.media3.common.util.Log;
|
import androidx.media3.common.util.Log;
|
||||||
import androidx.media3.common.util.UnstableApi;
|
import androidx.media3.common.util.UnstableApi;
|
||||||
import androidx.media3.common.util.Util;
|
import androidx.media3.common.util.Util;
|
||||||
|
import androidx.media3.exoplayer.analytics.PlayerId;
|
||||||
import androidx.media3.exoplayer.source.MediaSource.MediaPeriodId;
|
import androidx.media3.exoplayer.source.MediaSource.MediaPeriodId;
|
||||||
import androidx.media3.exoplayer.source.TrackGroupArray;
|
import androidx.media3.exoplayer.source.TrackGroupArray;
|
||||||
import androidx.media3.exoplayer.trackselection.ExoTrackSelection;
|
import androidx.media3.exoplayer.trackselection.ExoTrackSelection;
|
||||||
import androidx.media3.exoplayer.upstream.Allocator;
|
import androidx.media3.exoplayer.upstream.Allocator;
|
||||||
import androidx.media3.exoplayer.upstream.DefaultAllocator;
|
import androidx.media3.exoplayer.upstream.DefaultAllocator;
|
||||||
import com.google.errorprone.annotations.CanIgnoreReturnValue;
|
import com.google.errorprone.annotations.CanIgnoreReturnValue;
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
/** The default {@link LoadControl} implementation. */
|
/** The default {@link LoadControl} implementation. */
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
@ -183,8 +187,10 @@ public class DefaultLoadControl implements LoadControl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the target buffer size in bytes. If set to {@link C#LENGTH_UNSET}, the target buffer
|
* Sets the target buffer size in bytes for each player. The actual overall target buffer size
|
||||||
* size will be calculated based on the selected tracks.
|
* is this value multiplied by the number of players that use the load control simultaneously.
|
||||||
|
* If set to {@link C#LENGTH_UNSET}, the target buffer size of a player will be calculated based
|
||||||
|
* on the selected tracks of the player.
|
||||||
*
|
*
|
||||||
* @param targetBufferBytes The target buffer size in bytes.
|
* @param targetBufferBytes The target buffer size in bytes.
|
||||||
* @return This builder, for convenience.
|
* @return This builder, for convenience.
|
||||||
@ -262,9 +268,9 @@ public class DefaultLoadControl implements LoadControl {
|
|||||||
private final boolean prioritizeTimeOverSizeThresholds;
|
private final boolean prioritizeTimeOverSizeThresholds;
|
||||||
private final long backBufferDurationUs;
|
private final long backBufferDurationUs;
|
||||||
private final boolean retainBackBufferFromKeyframe;
|
private final boolean retainBackBufferFromKeyframe;
|
||||||
|
private final HashMap<PlayerId, PlayerLoadingState> loadingStates;
|
||||||
|
|
||||||
private int targetBufferBytes;
|
private long threadId;
|
||||||
private boolean isLoading;
|
|
||||||
|
|
||||||
/** Constructs a new instance, using the {@code DEFAULT_*} constants defined in this class. */
|
/** Constructs a new instance, using the {@code DEFAULT_*} constants defined in this class. */
|
||||||
public DefaultLoadControl() {
|
public DefaultLoadControl() {
|
||||||
@ -308,42 +314,53 @@ public class DefaultLoadControl implements LoadControl {
|
|||||||
this.bufferForPlaybackUs = Util.msToUs(bufferForPlaybackMs);
|
this.bufferForPlaybackUs = Util.msToUs(bufferForPlaybackMs);
|
||||||
this.bufferForPlaybackAfterRebufferUs = Util.msToUs(bufferForPlaybackAfterRebufferMs);
|
this.bufferForPlaybackAfterRebufferUs = Util.msToUs(bufferForPlaybackAfterRebufferMs);
|
||||||
this.targetBufferBytesOverwrite = targetBufferBytes;
|
this.targetBufferBytesOverwrite = targetBufferBytes;
|
||||||
this.targetBufferBytes =
|
|
||||||
targetBufferBytesOverwrite != C.LENGTH_UNSET
|
|
||||||
? targetBufferBytesOverwrite
|
|
||||||
: DEFAULT_MIN_BUFFER_SIZE;
|
|
||||||
this.prioritizeTimeOverSizeThresholds = prioritizeTimeOverSizeThresholds;
|
this.prioritizeTimeOverSizeThresholds = prioritizeTimeOverSizeThresholds;
|
||||||
this.backBufferDurationUs = Util.msToUs(backBufferDurationMs);
|
this.backBufferDurationUs = Util.msToUs(backBufferDurationMs);
|
||||||
this.retainBackBufferFromKeyframe = retainBackBufferFromKeyframe;
|
this.retainBackBufferFromKeyframe = retainBackBufferFromKeyframe;
|
||||||
|
loadingStates = new HashMap<>();
|
||||||
|
threadId = C.INDEX_UNSET;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPrepared() {
|
public void onPrepared(PlayerId playerId) {
|
||||||
reset(false);
|
long currentThreadId = Thread.currentThread().getId();
|
||||||
|
checkState(
|
||||||
|
threadId == C.INDEX_UNSET || threadId == currentThreadId,
|
||||||
|
"Players that share the same LoadControl must share the same playback thread. See"
|
||||||
|
+ " ExoPlayer.Builder.setPlaybackLooper(Looper).");
|
||||||
|
threadId = currentThreadId;
|
||||||
|
if (!loadingStates.containsKey(playerId)) {
|
||||||
|
loadingStates.put(playerId, new PlayerLoadingState());
|
||||||
|
}
|
||||||
|
resetPlayerLoadingState(playerId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onTracksSelected(
|
public void onTracksSelected(
|
||||||
|
PlayerId playerId,
|
||||||
Timeline timeline,
|
Timeline timeline,
|
||||||
MediaPeriodId mediaPeriodId,
|
MediaPeriodId mediaPeriodId,
|
||||||
Renderer[] renderers,
|
Renderer[] renderers,
|
||||||
TrackGroupArray trackGroups,
|
TrackGroupArray trackGroups,
|
||||||
ExoTrackSelection[] trackSelections) {
|
ExoTrackSelection[] trackSelections) {
|
||||||
targetBufferBytes =
|
checkNotNull(loadingStates.get(playerId)).targetBufferBytes =
|
||||||
targetBufferBytesOverwrite == C.LENGTH_UNSET
|
targetBufferBytesOverwrite == C.LENGTH_UNSET
|
||||||
? calculateTargetBufferBytes(renderers, trackSelections)
|
? calculateTargetBufferBytes(renderers, trackSelections)
|
||||||
: targetBufferBytesOverwrite;
|
: targetBufferBytesOverwrite;
|
||||||
allocator.setTargetBufferSize(targetBufferBytes);
|
updateAllocator();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onStopped() {
|
public void onStopped(PlayerId playerId) {
|
||||||
reset(true);
|
removePlayer(playerId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onReleased() {
|
public void onReleased(PlayerId playerId) {
|
||||||
reset(true);
|
removePlayer(playerId);
|
||||||
|
if (loadingStates.isEmpty()) {
|
||||||
|
threadId = C.INDEX_UNSET;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -352,19 +369,26 @@ public class DefaultLoadControl implements LoadControl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long getBackBufferDurationUs() {
|
public long getBackBufferDurationUs(PlayerId playerId) {
|
||||||
return backBufferDurationUs;
|
return backBufferDurationUs;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean retainBackBufferFromKeyframe() {
|
public boolean retainBackBufferFromKeyframe(PlayerId playerId) {
|
||||||
return retainBackBufferFromKeyframe;
|
return retainBackBufferFromKeyframe;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean shouldContinueLoading(
|
public boolean shouldContinueLoading(
|
||||||
long playbackPositionUs, long bufferedDurationUs, float playbackSpeed) {
|
PlayerId playerId,
|
||||||
boolean targetBufferSizeReached = allocator.getTotalBytesAllocated() >= targetBufferBytes;
|
Timeline timeline,
|
||||||
|
MediaPeriodId mediaPeriodId,
|
||||||
|
long playbackPositionUs,
|
||||||
|
long bufferedDurationUs,
|
||||||
|
float playbackSpeed) {
|
||||||
|
PlayerLoadingState playerLoadingState = checkNotNull(loadingStates.get(playerId));
|
||||||
|
boolean targetBufferSizeReached =
|
||||||
|
allocator.getTotalBytesAllocated() >= calculateTotalTargetBufferBytes();
|
||||||
long minBufferUs = this.minBufferUs;
|
long minBufferUs = this.minBufferUs;
|
||||||
if (playbackSpeed > 1) {
|
if (playbackSpeed > 1) {
|
||||||
// The playback speed is faster than real time, so scale up the minimum required media
|
// The playback speed is faster than real time, so scale up the minimum required media
|
||||||
@ -376,20 +400,21 @@ public class DefaultLoadControl implements LoadControl {
|
|||||||
// Prevent playback from getting stuck if minBufferUs is too small.
|
// Prevent playback from getting stuck if minBufferUs is too small.
|
||||||
minBufferUs = max(minBufferUs, 500_000);
|
minBufferUs = max(minBufferUs, 500_000);
|
||||||
if (bufferedDurationUs < minBufferUs) {
|
if (bufferedDurationUs < minBufferUs) {
|
||||||
isLoading = prioritizeTimeOverSizeThresholds || !targetBufferSizeReached;
|
playerLoadingState.isLoading = prioritizeTimeOverSizeThresholds || !targetBufferSizeReached;
|
||||||
if (!isLoading && bufferedDurationUs < 500_000) {
|
if (!playerLoadingState.isLoading && bufferedDurationUs < 500_000) {
|
||||||
Log.w(
|
Log.w(
|
||||||
"DefaultLoadControl",
|
"DefaultLoadControl",
|
||||||
"Target buffer size reached with less than 500ms of buffered media data.");
|
"Target buffer size reached with less than 500ms of buffered media data.");
|
||||||
}
|
}
|
||||||
} else if (bufferedDurationUs >= maxBufferUs || targetBufferSizeReached) {
|
} else if (bufferedDurationUs >= maxBufferUs || targetBufferSizeReached) {
|
||||||
isLoading = false;
|
playerLoadingState.isLoading = false;
|
||||||
} // Else don't change the loading state.
|
} // Else don't change the loading state.
|
||||||
return isLoading;
|
return playerLoadingState.isLoading;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean shouldStartPlayback(
|
public boolean shouldStartPlayback(
|
||||||
|
PlayerId playerId,
|
||||||
Timeline timeline,
|
Timeline timeline,
|
||||||
MediaPeriodId mediaPeriodId,
|
MediaPeriodId mediaPeriodId,
|
||||||
long bufferedDurationUs,
|
long bufferedDurationUs,
|
||||||
@ -404,7 +429,7 @@ public class DefaultLoadControl implements LoadControl {
|
|||||||
return minBufferDurationUs <= 0
|
return minBufferDurationUs <= 0
|
||||||
|| bufferedDurationUs >= minBufferDurationUs
|
|| bufferedDurationUs >= minBufferDurationUs
|
||||||
|| (!prioritizeTimeOverSizeThresholds
|
|| (!prioritizeTimeOverSizeThresholds
|
||||||
&& allocator.getTotalBytesAllocated() >= targetBufferBytes);
|
&& allocator.getTotalBytesAllocated() >= calculateTotalTargetBufferBytes());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -426,14 +451,35 @@ public class DefaultLoadControl implements LoadControl {
|
|||||||
return max(DEFAULT_MIN_BUFFER_SIZE, targetBufferSize);
|
return max(DEFAULT_MIN_BUFFER_SIZE, targetBufferSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void reset(boolean resetAllocator) {
|
@VisibleForTesting
|
||||||
targetBufferBytes =
|
/* package */ int calculateTotalTargetBufferBytes() {
|
||||||
|
int totalTargetBufferBytes = 0;
|
||||||
|
for (PlayerLoadingState state : loadingStates.values()) {
|
||||||
|
totalTargetBufferBytes += state.targetBufferBytes;
|
||||||
|
}
|
||||||
|
return totalTargetBufferBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void resetPlayerLoadingState(PlayerId playerId) {
|
||||||
|
PlayerLoadingState playerLoadingState = checkNotNull(loadingStates.get(playerId));
|
||||||
|
playerLoadingState.targetBufferBytes =
|
||||||
targetBufferBytesOverwrite == C.LENGTH_UNSET
|
targetBufferBytesOverwrite == C.LENGTH_UNSET
|
||||||
? DEFAULT_MIN_BUFFER_SIZE
|
? DEFAULT_MIN_BUFFER_SIZE
|
||||||
: targetBufferBytesOverwrite;
|
: targetBufferBytesOverwrite;
|
||||||
isLoading = false;
|
playerLoadingState.isLoading = false;
|
||||||
if (resetAllocator) {
|
}
|
||||||
|
|
||||||
|
private void removePlayer(PlayerId playerId) {
|
||||||
|
if (loadingStates.remove(playerId) != null) {
|
||||||
|
updateAllocator();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateAllocator() {
|
||||||
|
if (loadingStates.isEmpty()) {
|
||||||
allocator.reset();
|
allocator.reset();
|
||||||
|
} else {
|
||||||
|
allocator.setTargetBufferSize(calculateTotalTargetBufferBytes());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -464,4 +510,9 @@ public class DefaultLoadControl implements LoadControl {
|
|||||||
private static void assertGreaterOrEqual(int value1, int value2, String name1, String name2) {
|
private static void assertGreaterOrEqual(int value1, int value2, String name1, String name2) {
|
||||||
Assertions.checkArgument(value1 >= value2, name1 + " cannot be less than " + name2);
|
Assertions.checkArgument(value1 >= value2, name1 + " cannot be less than " + name2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class PlayerLoadingState {
|
||||||
|
public boolean isLoading;
|
||||||
|
public int targetBufferBytes;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -43,7 +43,8 @@ public final class DefaultAllocator implements Allocator {
|
|||||||
* Constructs an instance without creating any {@link Allocation}s up front.
|
* Constructs an instance without creating any {@link Allocation}s up front.
|
||||||
*
|
*
|
||||||
* @param trimOnReset Whether memory is freed when the allocator is reset. Should be true unless
|
* @param trimOnReset Whether memory is freed when the allocator is reset. Should be true unless
|
||||||
* the allocator will be re-used by multiple player instances.
|
* the allocator will be re-used by multiple player instances. If set to false, trimming can
|
||||||
|
* be forced by calling {@link #setTargetBufferSize(int)} manually when required.
|
||||||
* @param individualAllocationSize The length of each individual {@link Allocation}.
|
* @param individualAllocationSize The length of each individual {@link Allocation}.
|
||||||
*/
|
*/
|
||||||
public DefaultAllocator(boolean trimOnReset, int individualAllocationSize) {
|
public DefaultAllocator(boolean trimOnReset, int individualAllocationSize) {
|
||||||
@ -56,7 +57,8 @@ public final class DefaultAllocator implements Allocator {
|
|||||||
* <p>Note: {@link Allocation}s created up front will never be discarded by {@link #trim()}.
|
* <p>Note: {@link Allocation}s created up front will never be discarded by {@link #trim()}.
|
||||||
*
|
*
|
||||||
* @param trimOnReset Whether memory is freed when the allocator is reset. Should be true unless
|
* @param trimOnReset Whether memory is freed when the allocator is reset. Should be true unless
|
||||||
* the allocator will be re-used by multiple player instances.
|
* the allocator will be re-used by multiple player instances. If set to false, trimming can
|
||||||
|
* be forced by calling {@link #setTargetBufferSize(int)} manually when required.
|
||||||
* @param individualAllocationSize The length of each individual {@link Allocation}.
|
* @param individualAllocationSize The length of each individual {@link Allocation}.
|
||||||
* @param initialAllocationCount The number of allocations to create up front.
|
* @param initialAllocationCount The number of allocations to create up front.
|
||||||
*/
|
*/
|
||||||
|
@ -18,12 +18,22 @@ package androidx.media3.exoplayer;
|
|||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
import androidx.media3.common.C;
|
import androidx.media3.common.C;
|
||||||
|
import androidx.media3.common.Format;
|
||||||
|
import androidx.media3.common.MediaItem;
|
||||||
|
import androidx.media3.common.MimeTypes;
|
||||||
import androidx.media3.common.Timeline;
|
import androidx.media3.common.Timeline;
|
||||||
|
import androidx.media3.common.TrackGroup;
|
||||||
import androidx.media3.common.util.Util;
|
import androidx.media3.common.util.Util;
|
||||||
import androidx.media3.exoplayer.DefaultLoadControl.Builder;
|
import androidx.media3.exoplayer.DefaultLoadControl.Builder;
|
||||||
|
import androidx.media3.exoplayer.analytics.PlayerId;
|
||||||
|
import androidx.media3.exoplayer.source.MediaSource;
|
||||||
|
import androidx.media3.exoplayer.source.SinglePeriodTimeline;
|
||||||
import androidx.media3.exoplayer.source.TrackGroupArray;
|
import androidx.media3.exoplayer.source.TrackGroupArray;
|
||||||
import androidx.media3.exoplayer.trackselection.ExoTrackSelection;
|
import androidx.media3.exoplayer.trackselection.ExoTrackSelection;
|
||||||
|
import androidx.media3.exoplayer.trackselection.FixedTrackSelection;
|
||||||
import androidx.media3.exoplayer.upstream.DefaultAllocator;
|
import androidx.media3.exoplayer.upstream.DefaultAllocator;
|
||||||
|
import androidx.media3.test.utils.FakeRenderer;
|
||||||
|
import androidx.media3.test.utils.FakeTimeline;
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
@ -41,27 +51,126 @@ public class DefaultLoadControlTest {
|
|||||||
private Builder builder;
|
private Builder builder;
|
||||||
private DefaultAllocator allocator;
|
private DefaultAllocator allocator;
|
||||||
private DefaultLoadControl loadControl;
|
private DefaultLoadControl loadControl;
|
||||||
|
private PlayerId playerId;
|
||||||
|
private Timeline timeline;
|
||||||
|
private MediaSource.MediaPeriodId mediaPeriodId;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() throws Exception {
|
public void setUp() throws Exception {
|
||||||
builder = new Builder();
|
builder = new Builder();
|
||||||
allocator = new DefaultAllocator(true, C.DEFAULT_BUFFER_SEGMENT_SIZE);
|
allocator = new DefaultAllocator(true, C.DEFAULT_BUFFER_SEGMENT_SIZE);
|
||||||
|
playerId =
|
||||||
|
Util.SDK_INT < 31
|
||||||
|
? new PlayerId(/* playerName= */ "")
|
||||||
|
: new PlayerId(/* logSessionId= */ null, /* playerName= */ "");
|
||||||
|
timeline =
|
||||||
|
new SinglePeriodTimeline(
|
||||||
|
/* durationUs= */ 10_000_000L,
|
||||||
|
/* isSeekable= */ true,
|
||||||
|
/* isDynamic= */ true,
|
||||||
|
/* useLiveConfiguration= */ false,
|
||||||
|
/* manifest= */ null,
|
||||||
|
MediaItem.EMPTY);
|
||||||
|
mediaPeriodId =
|
||||||
|
new MediaSource.MediaPeriodId(
|
||||||
|
timeline.getPeriod(/* periodIndex= */ 0, new Timeline.Period()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void shouldContinueLoading_untilMaxBufferExceeded() {
|
public void shouldContinueLoading_untilMaxBufferExceeded() {
|
||||||
build();
|
build();
|
||||||
|
assertThat(
|
||||||
|
loadControl.shouldContinueLoading(
|
||||||
|
playerId,
|
||||||
|
timeline,
|
||||||
|
mediaPeriodId,
|
||||||
|
/* playbackPositionUs= */ 0L,
|
||||||
|
/* bufferedDurationUs= */ 0L,
|
||||||
|
SPEED))
|
||||||
|
.isTrue();
|
||||||
|
assertThat(
|
||||||
|
loadControl.shouldContinueLoading(
|
||||||
|
playerId,
|
||||||
|
timeline,
|
||||||
|
mediaPeriodId,
|
||||||
|
/* playbackPositionUs= */ 0L,
|
||||||
|
MAX_BUFFER_US - 1,
|
||||||
|
SPEED))
|
||||||
|
.isTrue();
|
||||||
|
assertThat(
|
||||||
|
loadControl.shouldContinueLoading(
|
||||||
|
playerId,
|
||||||
|
timeline,
|
||||||
|
mediaPeriodId,
|
||||||
|
/* playbackPositionUs= */ 0L,
|
||||||
|
MAX_BUFFER_US,
|
||||||
|
SPEED))
|
||||||
|
.isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldContinueLoading_twoPlayers_loadingStatesAreSeparated() {
|
||||||
|
builder.setBufferDurationsMs(
|
||||||
|
/* minBufferMs= */ (int) Util.usToMs(MIN_BUFFER_US),
|
||||||
|
/* maxBufferMs= */ (int) Util.usToMs(MAX_BUFFER_US),
|
||||||
|
/* bufferForPlaybackMs= */ 0,
|
||||||
|
/* bufferForPlaybackAfterRebufferMs= */ 0);
|
||||||
|
build();
|
||||||
|
// A second player uses the load control.
|
||||||
|
PlayerId playerId2 = new PlayerId(/* playerName= */ "");
|
||||||
|
Timeline timeline2 = new FakeTimeline();
|
||||||
|
MediaSource.MediaPeriodId mediaPeriodId2 =
|
||||||
|
new MediaSource.MediaPeriodId(
|
||||||
|
timeline.getPeriod(/* periodIndex= */ 0, new Timeline.Period()));
|
||||||
|
loadControl.onPrepared(playerId2);
|
||||||
|
// First player is fully buffered. Buffer starts depleting until it falls under min size.
|
||||||
|
loadControl.shouldContinueLoading(
|
||||||
|
playerId, timeline, mediaPeriodId, /* playbackPositionUs= */ 0L, MAX_BUFFER_US, SPEED);
|
||||||
|
// Second player fell below min size and starts loading until max size is reached.
|
||||||
|
loadControl.shouldContinueLoading(
|
||||||
|
playerId2,
|
||||||
|
timeline2,
|
||||||
|
mediaPeriodId2,
|
||||||
|
/* playbackPositionUs= */ 0L,
|
||||||
|
MIN_BUFFER_US - 1,
|
||||||
|
SPEED);
|
||||||
|
|
||||||
assertThat(
|
assertThat(
|
||||||
loadControl.shouldContinueLoading(
|
loadControl.shouldContinueLoading(
|
||||||
/* playbackPositionUs= */ 0, /* bufferedDurationUs= */ 0, SPEED))
|
playerId,
|
||||||
|
timeline,
|
||||||
|
mediaPeriodId,
|
||||||
|
/* playbackPositionUs= */ 0L,
|
||||||
|
MAX_BUFFER_US - 1,
|
||||||
|
SPEED))
|
||||||
|
.isFalse();
|
||||||
|
assertThat(
|
||||||
|
loadControl.shouldContinueLoading(
|
||||||
|
playerId2,
|
||||||
|
timeline2,
|
||||||
|
mediaPeriodId2,
|
||||||
|
/* playbackPositionUs= */ 0L,
|
||||||
|
MIN_BUFFER_US,
|
||||||
|
SPEED))
|
||||||
.isTrue();
|
.isTrue();
|
||||||
assertThat(
|
assertThat(
|
||||||
loadControl.shouldContinueLoading(
|
loadControl.shouldContinueLoading(
|
||||||
/* playbackPositionUs= */ 0, MAX_BUFFER_US - 1, SPEED))
|
playerId,
|
||||||
.isTrue();
|
timeline,
|
||||||
assertThat(loadControl.shouldContinueLoading(/* playbackPositionUs= */ 0, MAX_BUFFER_US, SPEED))
|
mediaPeriodId,
|
||||||
|
/* playbackPositionUs= */ 0L,
|
||||||
|
MIN_BUFFER_US,
|
||||||
|
SPEED))
|
||||||
.isFalse();
|
.isFalse();
|
||||||
|
assertThat(
|
||||||
|
loadControl.shouldContinueLoading(
|
||||||
|
playerId2,
|
||||||
|
timeline2,
|
||||||
|
mediaPeriodId2,
|
||||||
|
/* playbackPositionUs= */ 0L,
|
||||||
|
MAX_BUFFER_US - 1,
|
||||||
|
SPEED))
|
||||||
|
.isTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -73,17 +182,41 @@ public class DefaultLoadControlTest {
|
|||||||
/* bufferForPlaybackAfterRebufferMs= */ 0);
|
/* bufferForPlaybackAfterRebufferMs= */ 0);
|
||||||
build();
|
build();
|
||||||
|
|
||||||
assertThat(loadControl.shouldContinueLoading(/* playbackPositionUs= */ 0, MAX_BUFFER_US, SPEED))
|
assertThat(
|
||||||
|
loadControl.shouldContinueLoading(
|
||||||
|
playerId,
|
||||||
|
timeline,
|
||||||
|
mediaPeriodId,
|
||||||
|
/* playbackPositionUs= */ 0L,
|
||||||
|
MAX_BUFFER_US,
|
||||||
|
SPEED))
|
||||||
.isFalse();
|
.isFalse();
|
||||||
assertThat(
|
assertThat(
|
||||||
loadControl.shouldContinueLoading(
|
loadControl.shouldContinueLoading(
|
||||||
/* playbackPositionUs= */ 0, MAX_BUFFER_US - 1, SPEED))
|
playerId,
|
||||||
.isFalse();
|
timeline,
|
||||||
assertThat(loadControl.shouldContinueLoading(/* playbackPositionUs= */ 0, MIN_BUFFER_US, SPEED))
|
mediaPeriodId,
|
||||||
|
/* playbackPositionUs= */ 0L,
|
||||||
|
MAX_BUFFER_US - 1,
|
||||||
|
SPEED))
|
||||||
.isFalse();
|
.isFalse();
|
||||||
assertThat(
|
assertThat(
|
||||||
loadControl.shouldContinueLoading(
|
loadControl.shouldContinueLoading(
|
||||||
/* playbackPositionUs= */ 0, MIN_BUFFER_US - 1, SPEED))
|
playerId,
|
||||||
|
timeline,
|
||||||
|
mediaPeriodId,
|
||||||
|
/* playbackPositionUs= */ 0L,
|
||||||
|
MIN_BUFFER_US,
|
||||||
|
SPEED))
|
||||||
|
.isFalse();
|
||||||
|
assertThat(
|
||||||
|
loadControl.shouldContinueLoading(
|
||||||
|
playerId,
|
||||||
|
timeline,
|
||||||
|
mediaPeriodId,
|
||||||
|
/* playbackPositionUs= */ 0L,
|
||||||
|
MIN_BUFFER_US - 1,
|
||||||
|
SPEED))
|
||||||
.isTrue();
|
.isTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -96,13 +229,27 @@ public class DefaultLoadControlTest {
|
|||||||
/* bufferForPlaybackAfterRebufferMs= */ 0);
|
/* bufferForPlaybackAfterRebufferMs= */ 0);
|
||||||
build();
|
build();
|
||||||
|
|
||||||
assertThat(loadControl.shouldContinueLoading(/* playbackPositionUs= */ 0, MAX_BUFFER_US, SPEED))
|
assertThat(
|
||||||
|
loadControl.shouldContinueLoading(
|
||||||
|
playerId,
|
||||||
|
timeline,
|
||||||
|
mediaPeriodId,
|
||||||
|
/* playbackPositionUs= */ 0L,
|
||||||
|
MAX_BUFFER_US,
|
||||||
|
SPEED))
|
||||||
.isFalse();
|
.isFalse();
|
||||||
assertThat(
|
assertThat(
|
||||||
loadControl.shouldContinueLoading(
|
loadControl.shouldContinueLoading(
|
||||||
/* playbackPositionUs= */ 0, 5 * C.MICROS_PER_SECOND, SPEED))
|
playerId,
|
||||||
|
timeline,
|
||||||
|
mediaPeriodId,
|
||||||
|
/* playbackPositionUs= */ 0L,
|
||||||
|
5 * C.MICROS_PER_SECOND,
|
||||||
|
SPEED))
|
||||||
.isFalse();
|
.isFalse();
|
||||||
assertThat(loadControl.shouldContinueLoading(/* playbackPositionUs= */ 0, 500L, SPEED))
|
assertThat(
|
||||||
|
loadControl.shouldContinueLoading(
|
||||||
|
playerId, timeline, mediaPeriodId, /* playbackPositionUs= */ 0L, 500L, SPEED))
|
||||||
.isTrue();
|
.isTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -119,15 +266,39 @@ public class DefaultLoadControlTest {
|
|||||||
|
|
||||||
assertThat(
|
assertThat(
|
||||||
loadControl.shouldContinueLoading(
|
loadControl.shouldContinueLoading(
|
||||||
/* playbackPositionUs= */ 0, /* bufferedDurationUs= */ 0, SPEED))
|
playerId,
|
||||||
|
timeline,
|
||||||
|
mediaPeriodId,
|
||||||
|
/* playbackPositionUs= */ 0L,
|
||||||
|
/* bufferedDurationUs= */ 0L,
|
||||||
|
SPEED))
|
||||||
.isTrue();
|
.isTrue();
|
||||||
assertThat(
|
assertThat(
|
||||||
loadControl.shouldContinueLoading(
|
loadControl.shouldContinueLoading(
|
||||||
/* playbackPositionUs= */ 0, MIN_BUFFER_US - 1, SPEED))
|
playerId,
|
||||||
|
timeline,
|
||||||
|
mediaPeriodId,
|
||||||
|
/* playbackPositionUs= */ 0L,
|
||||||
|
MIN_BUFFER_US - 1,
|
||||||
|
SPEED))
|
||||||
.isTrue();
|
.isTrue();
|
||||||
assertThat(loadControl.shouldContinueLoading(/* playbackPositionUs= */ 0, MIN_BUFFER_US, SPEED))
|
assertThat(
|
||||||
|
loadControl.shouldContinueLoading(
|
||||||
|
playerId,
|
||||||
|
timeline,
|
||||||
|
mediaPeriodId,
|
||||||
|
/* playbackPositionUs= */ 0L,
|
||||||
|
MIN_BUFFER_US,
|
||||||
|
SPEED))
|
||||||
.isFalse();
|
.isFalse();
|
||||||
assertThat(loadControl.shouldContinueLoading(/* playbackPositionUs= */ 0, MAX_BUFFER_US, SPEED))
|
assertThat(
|
||||||
|
loadControl.shouldContinueLoading(
|
||||||
|
playerId,
|
||||||
|
timeline,
|
||||||
|
mediaPeriodId,
|
||||||
|
/* playbackPositionUs= */ 0L,
|
||||||
|
MAX_BUFFER_US,
|
||||||
|
SPEED))
|
||||||
.isFalse();
|
.isFalse();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -140,21 +311,50 @@ public class DefaultLoadControlTest {
|
|||||||
// Put loadControl in buffering state.
|
// Put loadControl in buffering state.
|
||||||
assertThat(
|
assertThat(
|
||||||
loadControl.shouldContinueLoading(
|
loadControl.shouldContinueLoading(
|
||||||
/* playbackPositionUs= */ 0, /* bufferedDurationUs= */ 0, SPEED))
|
playerId,
|
||||||
|
timeline,
|
||||||
|
mediaPeriodId,
|
||||||
|
/* playbackPositionUs= */ 0L,
|
||||||
|
/* bufferedDurationUs= */ 0L,
|
||||||
|
SPEED))
|
||||||
.isTrue();
|
.isTrue();
|
||||||
makeSureTargetBufferBytesReached();
|
makeSureTargetBufferBytesReached();
|
||||||
|
|
||||||
assertThat(
|
assertThat(
|
||||||
loadControl.shouldContinueLoading(
|
loadControl.shouldContinueLoading(
|
||||||
/* playbackPositionUs= */ 0, /* bufferedDurationUs= */ 0, SPEED))
|
playerId,
|
||||||
|
timeline,
|
||||||
|
mediaPeriodId,
|
||||||
|
/* playbackPositionUs= */ 0L,
|
||||||
|
/* bufferedDurationUs= */ 0L,
|
||||||
|
SPEED))
|
||||||
.isFalse();
|
.isFalse();
|
||||||
assertThat(
|
assertThat(
|
||||||
loadControl.shouldContinueLoading(
|
loadControl.shouldContinueLoading(
|
||||||
/* playbackPositionUs= */ 0, MIN_BUFFER_US - 1, SPEED))
|
playerId,
|
||||||
|
timeline,
|
||||||
|
mediaPeriodId,
|
||||||
|
/* playbackPositionUs= */ 0L,
|
||||||
|
MIN_BUFFER_US - 1,
|
||||||
|
SPEED))
|
||||||
.isFalse();
|
.isFalse();
|
||||||
assertThat(loadControl.shouldContinueLoading(/* playbackPositionUs= */ 0, MIN_BUFFER_US, SPEED))
|
assertThat(
|
||||||
|
loadControl.shouldContinueLoading(
|
||||||
|
playerId,
|
||||||
|
timeline,
|
||||||
|
mediaPeriodId,
|
||||||
|
/* playbackPositionUs= */ 0L,
|
||||||
|
MIN_BUFFER_US,
|
||||||
|
SPEED))
|
||||||
.isFalse();
|
.isFalse();
|
||||||
assertThat(loadControl.shouldContinueLoading(/* playbackPositionUs= */ 0, MAX_BUFFER_US, SPEED))
|
assertThat(
|
||||||
|
loadControl.shouldContinueLoading(
|
||||||
|
playerId,
|
||||||
|
timeline,
|
||||||
|
mediaPeriodId,
|
||||||
|
/* playbackPositionUs= */ 0L,
|
||||||
|
MAX_BUFFER_US,
|
||||||
|
SPEED))
|
||||||
.isFalse();
|
.isFalse();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -168,28 +368,47 @@ public class DefaultLoadControlTest {
|
|||||||
build();
|
build();
|
||||||
|
|
||||||
// At normal playback speed, we stop buffering when the buffer reaches the minimum.
|
// At normal playback speed, we stop buffering when the buffer reaches the minimum.
|
||||||
assertThat(loadControl.shouldContinueLoading(/* playbackPositionUs= */ 0, MIN_BUFFER_US, SPEED))
|
assertThat(
|
||||||
|
loadControl.shouldContinueLoading(
|
||||||
|
playerId,
|
||||||
|
timeline,
|
||||||
|
mediaPeriodId,
|
||||||
|
/* playbackPositionUs= */ 0L,
|
||||||
|
MIN_BUFFER_US,
|
||||||
|
SPEED))
|
||||||
.isFalse();
|
.isFalse();
|
||||||
// At double playback speed, we continue loading.
|
// At double playback speed, we continue loading.
|
||||||
assertThat(
|
assertThat(
|
||||||
loadControl.shouldContinueLoading(
|
loadControl.shouldContinueLoading(
|
||||||
/* playbackPositionUs= */ 0, MIN_BUFFER_US, /* playbackSpeed= */ 2f))
|
playerId,
|
||||||
|
timeline,
|
||||||
|
mediaPeriodId,
|
||||||
|
/* playbackPositionUs= */ 0L,
|
||||||
|
MIN_BUFFER_US,
|
||||||
|
/* playbackSpeed= */ 2f))
|
||||||
.isTrue();
|
.isTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void shouldContinueLoading_withNoSelectedTracks_returnsTrue() {
|
public void shouldContinueLoading_withNoSelectedTracks_returnsTrue() {
|
||||||
loadControl = builder.build();
|
loadControl = builder.build();
|
||||||
|
loadControl.onPrepared(playerId);
|
||||||
loadControl.onTracksSelected(
|
loadControl.onTracksSelected(
|
||||||
Timeline.EMPTY,
|
playerId,
|
||||||
LoadControl.EMPTY_MEDIA_PERIOD_ID,
|
timeline,
|
||||||
|
mediaPeriodId,
|
||||||
new Renderer[0],
|
new Renderer[0],
|
||||||
TrackGroupArray.EMPTY,
|
TrackGroupArray.EMPTY,
|
||||||
new ExoTrackSelection[0]);
|
new ExoTrackSelection[0]);
|
||||||
|
|
||||||
assertThat(
|
assertThat(
|
||||||
loadControl.shouldContinueLoading(
|
loadControl.shouldContinueLoading(
|
||||||
/* playbackPositionUs= */ 0, /* bufferedDurationUs= */ 0, /* playbackSpeed= */ 1f))
|
playerId,
|
||||||
|
timeline,
|
||||||
|
mediaPeriodId,
|
||||||
|
/* playbackPositionUs= */ 0L,
|
||||||
|
/* bufferedDurationUs= */ 0L,
|
||||||
|
/* playbackSpeed= */ 1f))
|
||||||
.isTrue();
|
.isTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -199,7 +418,12 @@ public class DefaultLoadControlTest {
|
|||||||
|
|
||||||
assertThat(
|
assertThat(
|
||||||
loadControl.shouldContinueLoading(
|
loadControl.shouldContinueLoading(
|
||||||
/* playbackPositionUs= */ 0, MAX_BUFFER_US, /* playbackSpeed= */ 100f))
|
playerId,
|
||||||
|
timeline,
|
||||||
|
mediaPeriodId,
|
||||||
|
/* playbackPositionUs= */ 0L,
|
||||||
|
MAX_BUFFER_US,
|
||||||
|
/* playbackSpeed= */ 100f))
|
||||||
.isFalse();
|
.isFalse();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -209,8 +433,9 @@ public class DefaultLoadControlTest {
|
|||||||
|
|
||||||
assertThat(
|
assertThat(
|
||||||
loadControl.shouldStartPlayback(
|
loadControl.shouldStartPlayback(
|
||||||
Timeline.EMPTY,
|
playerId,
|
||||||
LoadControl.EMPTY_MEDIA_PERIOD_ID,
|
timeline,
|
||||||
|
mediaPeriodId,
|
||||||
MIN_BUFFER_US,
|
MIN_BUFFER_US,
|
||||||
SPEED,
|
SPEED,
|
||||||
/* rebuffering= */ false,
|
/* rebuffering= */ false,
|
||||||
@ -230,18 +455,20 @@ public class DefaultLoadControlTest {
|
|||||||
|
|
||||||
assertThat(
|
assertThat(
|
||||||
loadControl.shouldStartPlayback(
|
loadControl.shouldStartPlayback(
|
||||||
Timeline.EMPTY,
|
playerId,
|
||||||
LoadControl.EMPTY_MEDIA_PERIOD_ID,
|
timeline,
|
||||||
/* bufferedDurationUs= */ 2_999_999,
|
mediaPeriodId,
|
||||||
|
/* bufferedDurationUs= */ 2_999_999L,
|
||||||
SPEED,
|
SPEED,
|
||||||
/* rebuffering= */ false,
|
/* rebuffering= */ false,
|
||||||
/* targetLiveOffsetUs= */ C.TIME_UNSET))
|
/* targetLiveOffsetUs= */ C.TIME_UNSET))
|
||||||
.isFalse();
|
.isFalse();
|
||||||
assertThat(
|
assertThat(
|
||||||
loadControl.shouldStartPlayback(
|
loadControl.shouldStartPlayback(
|
||||||
Timeline.EMPTY,
|
playerId,
|
||||||
LoadControl.EMPTY_MEDIA_PERIOD_ID,
|
timeline,
|
||||||
/* bufferedDurationUs= */ 3_000_000,
|
mediaPeriodId,
|
||||||
|
/* bufferedDurationUs= */ 3_000_000L,
|
||||||
SPEED,
|
SPEED,
|
||||||
/* rebuffering= */ false,
|
/* rebuffering= */ false,
|
||||||
/* targetLiveOffsetUs= */ C.TIME_UNSET))
|
/* targetLiveOffsetUs= */ C.TIME_UNSET))
|
||||||
@ -259,21 +486,23 @@ public class DefaultLoadControlTest {
|
|||||||
|
|
||||||
assertThat(
|
assertThat(
|
||||||
loadControl.shouldStartPlayback(
|
loadControl.shouldStartPlayback(
|
||||||
Timeline.EMPTY,
|
playerId,
|
||||||
LoadControl.EMPTY_MEDIA_PERIOD_ID,
|
timeline,
|
||||||
/* bufferedDurationUs= */ 499_999,
|
mediaPeriodId,
|
||||||
|
/* bufferedDurationUs= */ 499_999L,
|
||||||
SPEED,
|
SPEED,
|
||||||
/* rebuffering= */ true,
|
/* rebuffering= */ true,
|
||||||
/* targetLiveOffsetUs= */ 1_000_000))
|
/* targetLiveOffsetUs= */ 1_000_000L))
|
||||||
.isFalse();
|
.isFalse();
|
||||||
assertThat(
|
assertThat(
|
||||||
loadControl.shouldStartPlayback(
|
loadControl.shouldStartPlayback(
|
||||||
Timeline.EMPTY,
|
playerId,
|
||||||
LoadControl.EMPTY_MEDIA_PERIOD_ID,
|
timeline,
|
||||||
/* bufferedDurationUs= */ 500_000,
|
mediaPeriodId,
|
||||||
|
/* bufferedDurationUs= */ 500_000L,
|
||||||
SPEED,
|
SPEED,
|
||||||
/* rebuffering= */ true,
|
/* rebuffering= */ true,
|
||||||
/* targetLiveOffsetUs= */ 1_000_000))
|
/* targetLiveOffsetUs= */ 1_000_000L))
|
||||||
.isTrue();
|
.isTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -289,18 +518,20 @@ public class DefaultLoadControlTest {
|
|||||||
|
|
||||||
assertThat(
|
assertThat(
|
||||||
loadControl.shouldStartPlayback(
|
loadControl.shouldStartPlayback(
|
||||||
Timeline.EMPTY,
|
playerId,
|
||||||
LoadControl.EMPTY_MEDIA_PERIOD_ID,
|
timeline,
|
||||||
/* bufferedDurationUs= */ 3_999_999,
|
mediaPeriodId,
|
||||||
|
/* bufferedDurationUs= */ 3_999_999L,
|
||||||
SPEED,
|
SPEED,
|
||||||
/* rebuffering= */ true,
|
/* rebuffering= */ true,
|
||||||
/* targetLiveOffsetUs= */ C.TIME_UNSET))
|
/* targetLiveOffsetUs= */ C.TIME_UNSET))
|
||||||
.isFalse();
|
.isFalse();
|
||||||
assertThat(
|
assertThat(
|
||||||
loadControl.shouldStartPlayback(
|
loadControl.shouldStartPlayback(
|
||||||
Timeline.EMPTY,
|
playerId,
|
||||||
LoadControl.EMPTY_MEDIA_PERIOD_ID,
|
timeline,
|
||||||
/* bufferedDurationUs= */ 4_000_000,
|
mediaPeriodId,
|
||||||
|
/* bufferedDurationUs= */ 4_000_000L,
|
||||||
SPEED,
|
SPEED,
|
||||||
/* rebuffering= */ true,
|
/* rebuffering= */ true,
|
||||||
/* targetLiveOffsetUs= */ C.TIME_UNSET))
|
/* targetLiveOffsetUs= */ C.TIME_UNSET))
|
||||||
@ -318,29 +549,106 @@ public class DefaultLoadControlTest {
|
|||||||
|
|
||||||
assertThat(
|
assertThat(
|
||||||
loadControl.shouldStartPlayback(
|
loadControl.shouldStartPlayback(
|
||||||
Timeline.EMPTY,
|
playerId,
|
||||||
LoadControl.EMPTY_MEDIA_PERIOD_ID,
|
timeline,
|
||||||
/* bufferedDurationUs= */ 499_999,
|
mediaPeriodId,
|
||||||
|
/* bufferedDurationUs= */ 499_999L,
|
||||||
SPEED,
|
SPEED,
|
||||||
/* rebuffering= */ true,
|
/* rebuffering= */ true,
|
||||||
/* targetLiveOffsetUs= */ 1_000_000))
|
/* targetLiveOffsetUs= */ 1_000_000L))
|
||||||
.isFalse();
|
.isFalse();
|
||||||
assertThat(
|
assertThat(
|
||||||
loadControl.shouldStartPlayback(
|
loadControl.shouldStartPlayback(
|
||||||
Timeline.EMPTY,
|
playerId,
|
||||||
LoadControl.EMPTY_MEDIA_PERIOD_ID,
|
timeline,
|
||||||
/* bufferedDurationUs= */ 500_000,
|
mediaPeriodId,
|
||||||
|
/* bufferedDurationUs= */ 500_000L,
|
||||||
SPEED,
|
SPEED,
|
||||||
/* rebuffering= */ true,
|
/* rebuffering= */ true,
|
||||||
/* targetLiveOffsetUs= */ 1_000_000))
|
/* targetLiveOffsetUs= */ 1_000_000L))
|
||||||
.isTrue();
|
.isTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onPrepared_updatesTargetBufferBytes_correctDefaultTargetBufferSize() {
|
||||||
|
PlayerId playerId2 = new PlayerId(/* playerName= */ "");
|
||||||
|
loadControl = builder.setAllocator(allocator).build();
|
||||||
|
|
||||||
|
loadControl.onPrepared(playerId);
|
||||||
|
loadControl.onPrepared(playerId2);
|
||||||
|
|
||||||
|
assertThat(loadControl.calculateTotalTargetBufferBytes())
|
||||||
|
.isEqualTo(2 * DefaultLoadControl.DEFAULT_MIN_BUFFER_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onTrackSelected_updatesTargetBufferBytes_correctTargetBufferSizeFromTrackType() {
|
||||||
|
PlayerId playerId2 = new PlayerId(/* playerName= */ "");
|
||||||
|
loadControl = builder.setAllocator(allocator).build();
|
||||||
|
loadControl.onPrepared(playerId);
|
||||||
|
loadControl.onPrepared(playerId2);
|
||||||
|
Timeline timeline2 = new FakeTimeline();
|
||||||
|
MediaSource.MediaPeriodId mediaPeriodId2 =
|
||||||
|
new MediaSource.MediaPeriodId(
|
||||||
|
timeline.getPeriod(/* periodIndex= */ 0, new Timeline.Period()));
|
||||||
|
TrackGroup videoTrackGroup =
|
||||||
|
new TrackGroup(new Format.Builder().setSampleMimeType(MimeTypes.VIDEO_H264).build());
|
||||||
|
TrackGroupArray videoTrackGroupArray = new TrackGroupArray(videoTrackGroup);
|
||||||
|
Renderer[] videoRenderer = new Renderer[] {new FakeRenderer(C.TRACK_TYPE_VIDEO)};
|
||||||
|
TrackGroup audioTrackGroup =
|
||||||
|
new TrackGroup(new Format.Builder().setSampleMimeType(MimeTypes.AUDIO_AAC).build());
|
||||||
|
TrackGroupArray audioTrackGroupArray = new TrackGroupArray(audioTrackGroup);
|
||||||
|
Renderer[] audioRenderer = new Renderer[] {new FakeRenderer(C.TRACK_TYPE_AUDIO)};
|
||||||
|
|
||||||
|
loadControl.onTracksSelected(
|
||||||
|
playerId,
|
||||||
|
timeline,
|
||||||
|
mediaPeriodId,
|
||||||
|
videoRenderer,
|
||||||
|
videoTrackGroupArray,
|
||||||
|
new ExoTrackSelection[] {new FixedTrackSelection(videoTrackGroup, /* track= */ 0)});
|
||||||
|
loadControl.onTracksSelected(
|
||||||
|
playerId2,
|
||||||
|
timeline2,
|
||||||
|
mediaPeriodId2,
|
||||||
|
audioRenderer,
|
||||||
|
audioTrackGroupArray,
|
||||||
|
new ExoTrackSelection[] {new FixedTrackSelection(audioTrackGroup, /* track= */ 0)});
|
||||||
|
|
||||||
|
assertThat(loadControl.calculateTotalTargetBufferBytes())
|
||||||
|
.isEqualTo((2000 * C.DEFAULT_BUFFER_SEGMENT_SIZE) + (200 * C.DEFAULT_BUFFER_SEGMENT_SIZE));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onRelease_removesLoadingStateOfPlayer() {
|
||||||
|
PlayerId playerId2 = new PlayerId(/* playerName= */ "");
|
||||||
|
loadControl = builder.setAllocator(allocator).build();
|
||||||
|
loadControl.onPrepared(playerId);
|
||||||
|
loadControl.onPrepared(playerId2);
|
||||||
|
assertThat(loadControl.calculateTotalTargetBufferBytes())
|
||||||
|
.isEqualTo(2 * DefaultLoadControl.DEFAULT_MIN_BUFFER_SIZE);
|
||||||
|
|
||||||
|
loadControl.onReleased(playerId);
|
||||||
|
|
||||||
|
assertThat(loadControl.calculateTotalTargetBufferBytes())
|
||||||
|
.isEqualTo(DefaultLoadControl.DEFAULT_MIN_BUFFER_SIZE);
|
||||||
|
|
||||||
|
loadControl.onReleased(playerId2);
|
||||||
|
|
||||||
|
assertThat(loadControl.calculateTotalTargetBufferBytes()).isEqualTo(0);
|
||||||
|
}
|
||||||
|
|
||||||
private void build() {
|
private void build() {
|
||||||
builder.setAllocator(allocator).setTargetBufferBytes(TARGET_BUFFER_BYTES);
|
builder.setAllocator(allocator).setTargetBufferBytes(TARGET_BUFFER_BYTES);
|
||||||
loadControl = builder.build();
|
loadControl = builder.build();
|
||||||
|
loadControl.onPrepared(playerId);
|
||||||
loadControl.onTracksSelected(
|
loadControl.onTracksSelected(
|
||||||
Timeline.EMPTY, LoadControl.EMPTY_MEDIA_PERIOD_ID, new Renderer[0], null, null);
|
playerId,
|
||||||
|
timeline,
|
||||||
|
mediaPeriodId,
|
||||||
|
new Renderer[0],
|
||||||
|
/* trackGroups= */ null,
|
||||||
|
/* trackSelections= */ null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void makeSureTargetBufferBytesReached() {
|
private void makeSureTargetBufferBytesReached() {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user