parent
7ebfed505c
commit
3a7a665d5d
@ -34,6 +34,7 @@ import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Iterables;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
@ -94,7 +95,7 @@ public final class ClippedPlaybackTest {
|
||||
player.get().addListener(textCapturer);
|
||||
player.get().setMediaItem(mediaItem);
|
||||
player.get().prepare();
|
||||
player.get().play();
|
||||
playWhenLoadingIsDone(player.get());
|
||||
});
|
||||
|
||||
textCapturer.block();
|
||||
@ -146,6 +147,9 @@ public final class ClippedPlaybackTest {
|
||||
player.get().addListener(textCapturer);
|
||||
player.get().setMediaItems(mediaItems);
|
||||
player.get().prepare();
|
||||
// We don't need playWhenLoadingIsDone here because playback already waits at the end
|
||||
// of the first period for subtitles to be fully loaded beforetransitioning to the
|
||||
// second period.
|
||||
player.get().play();
|
||||
});
|
||||
|
||||
@ -157,6 +161,21 @@ public final class ClippedPlaybackTest {
|
||||
.isEqualTo("This is the first subtitle.");
|
||||
}
|
||||
|
||||
private static void playWhenLoadingIsDone(Player player) {
|
||||
AtomicBoolean loadingStarted = new AtomicBoolean(false);
|
||||
player.addListener(
|
||||
new Player.Listener() {
|
||||
@Override
|
||||
public void onEvents(Player player, Player.Events events) {
|
||||
if (events.contains(Player.EVENT_IS_LOADING_CHANGED)
|
||||
&& loadingStarted.getAndSet(player.isLoading())
|
||||
&& !player.isLoading()) {
|
||||
player.play();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static class TextCapturingPlaybackListener implements Player.Listener {
|
||||
|
||||
private final ConditionVariable playbackEnded;
|
||||
|
@ -15,38 +15,88 @@
|
||||
*/
|
||||
package androidx.media3.exoplayer.source;
|
||||
|
||||
import static androidx.media3.common.util.Assertions.checkArgument;
|
||||
import static java.lang.Math.min;
|
||||
|
||||
import androidx.media3.common.C;
|
||||
import androidx.media3.common.util.UnstableApi;
|
||||
import androidx.media3.exoplayer.LoadingInfo;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/** A {@link SequenceableLoader} that encapsulates multiple other {@link SequenceableLoader}s. */
|
||||
@UnstableApi
|
||||
public final class CompositeSequenceableLoader implements SequenceableLoader {
|
||||
|
||||
private final SequenceableLoader[] loaders;
|
||||
private final ImmutableList<SequenceableLoaderWithTrackTypes> loadersWithTrackTypes;
|
||||
private long lastAudioVideoBufferedPositionUs;
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link CompositeSequenceableLoader#CompositeSequenceableLoader(List, List)}
|
||||
* instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public CompositeSequenceableLoader(SequenceableLoader[] loaders) {
|
||||
this.loaders = loaders;
|
||||
this(
|
||||
ImmutableList.copyOf(loaders),
|
||||
Collections.nCopies(loaders.length, ImmutableList.of(C.TRACK_TYPE_UNKNOWN)));
|
||||
}
|
||||
|
||||
public CompositeSequenceableLoader(
|
||||
List<? extends SequenceableLoader> loaders,
|
||||
List<List<@C.TrackType Integer>> loaderTrackTypes) {
|
||||
ImmutableList.Builder<SequenceableLoaderWithTrackTypes> loaderAndTrackTypes =
|
||||
ImmutableList.builder();
|
||||
checkArgument(loaders.size() == loaderTrackTypes.size());
|
||||
for (int i = 0; i < loaders.size(); i++) {
|
||||
loaderAndTrackTypes.add(
|
||||
new SequenceableLoaderWithTrackTypes(loaders.get(i), loaderTrackTypes.get(i)));
|
||||
}
|
||||
this.loadersWithTrackTypes = loaderAndTrackTypes.build();
|
||||
this.lastAudioVideoBufferedPositionUs = C.TIME_UNSET;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getBufferedPositionUs() {
|
||||
long bufferedPositionUs = Long.MAX_VALUE;
|
||||
for (SequenceableLoader loader : loaders) {
|
||||
long bufferedPositionAudioVideoUs = Long.MAX_VALUE;
|
||||
for (int i = 0; i < loadersWithTrackTypes.size(); i++) {
|
||||
SequenceableLoaderWithTrackTypes loader = loadersWithTrackTypes.get(i);
|
||||
long loaderBufferedPositionUs = loader.getBufferedPositionUs();
|
||||
|
||||
if (loader.getTrackTypes().contains(C.TRACK_TYPE_AUDIO)
|
||||
|| loader.getTrackTypes().contains(C.TRACK_TYPE_VIDEO)
|
||||
|| loader.getTrackTypes().contains(C.TRACK_TYPE_IMAGE)) {
|
||||
if (loaderBufferedPositionUs != C.TIME_END_OF_SOURCE) {
|
||||
bufferedPositionAudioVideoUs =
|
||||
min(bufferedPositionAudioVideoUs, loaderBufferedPositionUs);
|
||||
}
|
||||
}
|
||||
if (loaderBufferedPositionUs != C.TIME_END_OF_SOURCE) {
|
||||
bufferedPositionUs = min(bufferedPositionUs, loaderBufferedPositionUs);
|
||||
}
|
||||
}
|
||||
return bufferedPositionUs == Long.MAX_VALUE ? C.TIME_END_OF_SOURCE : bufferedPositionUs;
|
||||
if (bufferedPositionAudioVideoUs != Long.MAX_VALUE) {
|
||||
lastAudioVideoBufferedPositionUs = bufferedPositionAudioVideoUs;
|
||||
return bufferedPositionAudioVideoUs;
|
||||
} else if (bufferedPositionUs != Long.MAX_VALUE) {
|
||||
// If lastAudioVideoBufferedPositionUs != C.TIME_UNSET, then we know there's at least one a/v
|
||||
// track (because this is the only time we end up assigning lastAudioVideoBufferedPositionUs
|
||||
// on a previous invocation).
|
||||
return lastAudioVideoBufferedPositionUs != C.TIME_UNSET
|
||||
? lastAudioVideoBufferedPositionUs
|
||||
: bufferedPositionUs;
|
||||
} else {
|
||||
return C.TIME_END_OF_SOURCE;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getNextLoadPositionUs() {
|
||||
long nextLoadPositionUs = Long.MAX_VALUE;
|
||||
for (SequenceableLoader loader : loaders) {
|
||||
for (int i = 0; i < loadersWithTrackTypes.size(); i++) {
|
||||
SequenceableLoaderWithTrackTypes loader = loadersWithTrackTypes.get(i);
|
||||
long loaderNextLoadPositionUs = loader.getNextLoadPositionUs();
|
||||
if (loaderNextLoadPositionUs != C.TIME_END_OF_SOURCE) {
|
||||
nextLoadPositionUs = min(nextLoadPositionUs, loaderNextLoadPositionUs);
|
||||
@ -57,8 +107,8 @@ public final class CompositeSequenceableLoader implements SequenceableLoader {
|
||||
|
||||
@Override
|
||||
public void reevaluateBuffer(long positionUs) {
|
||||
for (SequenceableLoader loader : loaders) {
|
||||
loader.reevaluateBuffer(positionUs);
|
||||
for (int i = 0; i < loadersWithTrackTypes.size(); i++) {
|
||||
loadersWithTrackTypes.get(i).reevaluateBuffer(positionUs);
|
||||
}
|
||||
}
|
||||
|
||||
@ -72,13 +122,13 @@ public final class CompositeSequenceableLoader implements SequenceableLoader {
|
||||
if (nextLoadPositionUs == C.TIME_END_OF_SOURCE) {
|
||||
break;
|
||||
}
|
||||
for (SequenceableLoader loader : loaders) {
|
||||
long loaderNextLoadPositionUs = loader.getNextLoadPositionUs();
|
||||
for (int i = 0; i < loadersWithTrackTypes.size(); i++) {
|
||||
long loaderNextLoadPositionUs = loadersWithTrackTypes.get(i).getNextLoadPositionUs();
|
||||
boolean isLoaderBehind =
|
||||
loaderNextLoadPositionUs != C.TIME_END_OF_SOURCE
|
||||
&& loaderNextLoadPositionUs <= loadingInfo.playbackPositionUs;
|
||||
if (loaderNextLoadPositionUs == nextLoadPositionUs || isLoaderBehind) {
|
||||
madeProgressThisIteration |= loader.continueLoading(loadingInfo);
|
||||
madeProgressThisIteration |= loadersWithTrackTypes.get(i).continueLoading(loadingInfo);
|
||||
}
|
||||
}
|
||||
madeProgress |= madeProgressThisIteration;
|
||||
@ -88,11 +138,54 @@ public final class CompositeSequenceableLoader implements SequenceableLoader {
|
||||
|
||||
@Override
|
||||
public boolean isLoading() {
|
||||
for (SequenceableLoader loader : loaders) {
|
||||
if (loader.isLoading()) {
|
||||
for (int i = 0; i < loadersWithTrackTypes.size(); i++) {
|
||||
if (loadersWithTrackTypes.get(i).isLoading()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static final class SequenceableLoaderWithTrackTypes implements SequenceableLoader {
|
||||
|
||||
private final SequenceableLoader loader;
|
||||
private final ImmutableList<@C.TrackType Integer> trackTypes;
|
||||
|
||||
public SequenceableLoaderWithTrackTypes(
|
||||
SequenceableLoader loader, List<@C.TrackType Integer> trackTypes) {
|
||||
this.loader = loader;
|
||||
this.trackTypes = ImmutableList.copyOf(trackTypes);
|
||||
}
|
||||
|
||||
public ImmutableList<@C.TrackType Integer> getTrackTypes() {
|
||||
return trackTypes;
|
||||
}
|
||||
|
||||
// SequenceableLoader implementation
|
||||
|
||||
@Override
|
||||
public long getBufferedPositionUs() {
|
||||
return loader.getBufferedPositionUs();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getNextLoadPositionUs() {
|
||||
return loader.getNextLoadPositionUs();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean continueLoading(LoadingInfo loadingInfo) {
|
||||
return loader.continueLoading(loadingInfo);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLoading() {
|
||||
return loader.isLoading();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reevaluateBuffer(long positionUs) {
|
||||
loader.reevaluateBuffer(positionUs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -15,17 +15,33 @@
|
||||
*/
|
||||
package androidx.media3.exoplayer.source;
|
||||
|
||||
import androidx.media3.common.C;
|
||||
import androidx.media3.common.util.UnstableApi;
|
||||
import java.util.List;
|
||||
|
||||
/** A factory to create composite {@link SequenceableLoader}s. */
|
||||
@UnstableApi
|
||||
public interface CompositeSequenceableLoaderFactory {
|
||||
|
||||
/** Returns an empty composite {@link SequenceableLoader}, with no delegate loaders. */
|
||||
SequenceableLoader empty();
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #empty()} for an empty composite loader, or {@link #create(List, List)}
|
||||
* for a non-empty one.
|
||||
*/
|
||||
@Deprecated
|
||||
SequenceableLoader createCompositeSequenceableLoader(SequenceableLoader... loaders);
|
||||
|
||||
/**
|
||||
* Creates a composite {@link SequenceableLoader}.
|
||||
*
|
||||
* @param loaders The sub-loaders that make up the {@link SequenceableLoader} to be built.
|
||||
* @param loaderTrackTypes The track types handled by each entry in {@code loaders}. Must be the
|
||||
* same as {@code loaders}.
|
||||
* @return A composite {@link SequenceableLoader} that comprises the given loaders.
|
||||
*/
|
||||
SequenceableLoader createCompositeSequenceableLoader(SequenceableLoader... loaders);
|
||||
SequenceableLoader create(
|
||||
List<? extends SequenceableLoader> loaders,
|
||||
List<List<@C.TrackType Integer>> loaderTrackTypes);
|
||||
}
|
||||
|
@ -15,7 +15,10 @@
|
||||
*/
|
||||
package androidx.media3.exoplayer.source;
|
||||
|
||||
import androidx.media3.common.C;
|
||||
import androidx.media3.common.util.UnstableApi;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import java.util.List;
|
||||
|
||||
/** Default implementation of {@link CompositeSequenceableLoaderFactory}. */
|
||||
@UnstableApi
|
||||
@ -23,7 +26,21 @@ public final class DefaultCompositeSequenceableLoaderFactory
|
||||
implements CompositeSequenceableLoaderFactory {
|
||||
|
||||
@Override
|
||||
public SequenceableLoader empty() {
|
||||
return new CompositeSequenceableLoader(ImmutableList.of(), ImmutableList.of());
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@Override
|
||||
@SuppressWarnings("deprecation") // Calling deprecated constructor
|
||||
public SequenceableLoader createCompositeSequenceableLoader(SequenceableLoader... loaders) {
|
||||
return new CompositeSequenceableLoader(loaders);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SequenceableLoader create(
|
||||
List<? extends SequenceableLoader> loaders,
|
||||
List<List<@C.TrackType Integer>> loaderTrackTypes) {
|
||||
return new CompositeSequenceableLoader(loaders, loaderTrackTypes);
|
||||
}
|
||||
}
|
||||
|
@ -29,6 +29,7 @@ import androidx.media3.exoplayer.source.chunk.Chunk;
|
||||
import androidx.media3.exoplayer.source.chunk.MediaChunk;
|
||||
import androidx.media3.exoplayer.source.chunk.MediaChunkIterator;
|
||||
import androidx.media3.exoplayer.trackselection.ExoTrackSelection;
|
||||
import com.google.common.collect.Lists;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
@ -58,8 +59,7 @@ import java.util.List;
|
||||
this.periods = periods;
|
||||
childrenPendingPreparation = new ArrayList<>();
|
||||
childTrackGroupByMergedTrackGroup = new HashMap<>();
|
||||
compositeSequenceableLoader =
|
||||
compositeSequenceableLoaderFactory.createCompositeSequenceableLoader();
|
||||
compositeSequenceableLoader = compositeSequenceableLoaderFactory.empty();
|
||||
streamPeriodIndices = new IdentityHashMap<>();
|
||||
enabledPeriods = new MediaPeriod[0];
|
||||
for (int i = 0; i < periods.length; i++) {
|
||||
@ -172,7 +172,9 @@ import java.util.List;
|
||||
// Update the local state.
|
||||
enabledPeriods = enabledPeriodsList.toArray(new MediaPeriod[0]);
|
||||
compositeSequenceableLoader =
|
||||
compositeSequenceableLoaderFactory.createCompositeSequenceableLoader(enabledPeriods);
|
||||
compositeSequenceableLoaderFactory.create(
|
||||
enabledPeriodsList,
|
||||
Lists.transform(enabledPeriodsList, period -> period.getTrackGroups().getTrackTypes()));
|
||||
return positionUs;
|
||||
}
|
||||
|
||||
|
@ -25,6 +25,7 @@ import androidx.media3.common.util.Log;
|
||||
import androidx.media3.common.util.UnstableApi;
|
||||
import androidx.media3.common.util.Util;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Lists;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@ -90,6 +91,11 @@ public final class TrackGroupArray implements Bundleable {
|
||||
return length == 0;
|
||||
}
|
||||
|
||||
/** Returns the {@link TrackGroup#type} of each track group in this array. */
|
||||
public ImmutableList<@C.TrackType Integer> getTrackTypes() {
|
||||
return ImmutableList.copyOf(Lists.transform(trackGroups, t -> t.type));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
if (hashCode == 0) {
|
||||
|
@ -22,6 +22,7 @@ import static com.google.common.truth.Truth.assertThat;
|
||||
import androidx.media3.common.C;
|
||||
import androidx.media3.exoplayer.LoadingInfo;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@ -41,7 +42,10 @@ public final class CompositeSequenceableLoaderTest {
|
||||
FakeSequenceableLoader loader2 =
|
||||
new FakeSequenceableLoader(/* bufferedPositionUs */ 1001, /* nextLoadPositionUs */ 2001);
|
||||
CompositeSequenceableLoader compositeSequenceableLoader =
|
||||
new CompositeSequenceableLoader(new SequenceableLoader[] {loader1, loader2});
|
||||
new CompositeSequenceableLoader(
|
||||
ImmutableList.of(loader1, loader2),
|
||||
ImmutableList.of(
|
||||
ImmutableList.of(C.TRACK_TYPE_AUDIO), ImmutableList.of(C.TRACK_TYPE_VIDEO)));
|
||||
assertThat(compositeSequenceableLoader.getBufferedPositionUs()).isEqualTo(1000);
|
||||
assertThat(compositeSequenceableLoader.isLoading()).isTrue();
|
||||
}
|
||||
@ -62,7 +66,12 @@ public final class CompositeSequenceableLoaderTest {
|
||||
/* bufferedPositionUs */ C.TIME_END_OF_SOURCE,
|
||||
/* nextLoadPositionUs */ C.TIME_END_OF_SOURCE);
|
||||
CompositeSequenceableLoader compositeSequenceableLoader =
|
||||
new CompositeSequenceableLoader(new SequenceableLoader[] {loader1, loader2, loader3});
|
||||
new CompositeSequenceableLoader(
|
||||
ImmutableList.of(loader1, loader2, loader3),
|
||||
ImmutableList.of(
|
||||
ImmutableList.of(C.TRACK_TYPE_AUDIO),
|
||||
ImmutableList.of(C.TRACK_TYPE_VIDEO),
|
||||
ImmutableList.of(C.TRACK_TYPE_AUDIO, C.TRACK_TYPE_VIDEO)));
|
||||
assertThat(compositeSequenceableLoader.getBufferedPositionUs()).isEqualTo(1000);
|
||||
assertThat(compositeSequenceableLoader.isLoading()).isTrue();
|
||||
}
|
||||
@ -83,11 +92,82 @@ public final class CompositeSequenceableLoaderTest {
|
||||
/* bufferedPositionUs */ C.TIME_END_OF_SOURCE,
|
||||
/* nextLoadPositionUs */ C.TIME_END_OF_SOURCE);
|
||||
CompositeSequenceableLoader compositeSequenceableLoader =
|
||||
new CompositeSequenceableLoader(new SequenceableLoader[] {loader1, loader2});
|
||||
new CompositeSequenceableLoader(
|
||||
ImmutableList.of(loader1, loader2),
|
||||
ImmutableList.of(
|
||||
ImmutableList.of(C.TRACK_TYPE_AUDIO), ImmutableList.of(C.TRACK_TYPE_VIDEO)));
|
||||
assertThat(compositeSequenceableLoader.getBufferedPositionUs()).isEqualTo(C.TIME_END_OF_SOURCE);
|
||||
assertThat(compositeSequenceableLoader.isLoading()).isFalse();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that {@link CompositeSequenceableLoader#getBufferedPositionUs()} returns the minimum
|
||||
* buffered position of loaders with audio or video tracks (if at least one loader has tracks of
|
||||
* these types).
|
||||
*/
|
||||
@Test
|
||||
public void getBufferedPositionUs_prefersLoadersWithAudioAndVideoTracks() {
|
||||
FakeSequenceableLoader loaderWithTextOnly =
|
||||
new FakeSequenceableLoader(/* bufferedPositionUs= */ 999, /* nextLoadPositionUs= */ 2000);
|
||||
FakeSequenceableLoader loaderWithAudioVideoAndText =
|
||||
new FakeSequenceableLoader(/* bufferedPositionUs= */ 1000, /* nextLoadPositionUs= */ 2000);
|
||||
CompositeSequenceableLoader compositeSequenceableLoader =
|
||||
new CompositeSequenceableLoader(
|
||||
ImmutableList.of(loaderWithTextOnly, loaderWithAudioVideoAndText),
|
||||
ImmutableList.of(
|
||||
ImmutableList.of(C.TRACK_TYPE_TEXT),
|
||||
ImmutableList.of(C.TRACK_TYPE_AUDIO, C.TRACK_TYPE_VIDEO, C.TRACK_TYPE_TEXT)));
|
||||
assertThat(compositeSequenceableLoader.getBufferedPositionUs()).isEqualTo(1000);
|
||||
assertThat(compositeSequenceableLoader.isLoading()).isTrue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that {@link CompositeSequenceableLoader#getBufferedPositionUs()} doesn't return {@link
|
||||
* C#TIME_END_OF_SOURCE} if only the A/V tracks have finished loading and text track is still
|
||||
* loading. Instead it keeps returning the last 'real' A/V buffered position, to avoid the
|
||||
* buffered position snapping back to the text track buffered position.
|
||||
*/
|
||||
@Test
|
||||
public void
|
||||
getBufferedPositionUs_prefersLoadersWithAudioAndVideoTracks_fallsBackToLastAVBufferedPositionWhenLoadingComplete() {
|
||||
FakeSequenceableLoader loaderWithTextOnly =
|
||||
new FakeSequenceableLoader(/* bufferedPositionUs= */ 500, /* nextLoadPositionUs= */ 2000);
|
||||
FakeSequenceableLoader loaderWithAudioVideoAndText =
|
||||
new FakeSequenceableLoader(
|
||||
/* bufferedPositionUs= */ 1000, /* nextLoadPositionUs= */ C.TIME_END_OF_SOURCE);
|
||||
CompositeSequenceableLoader compositeSequenceableLoader =
|
||||
new CompositeSequenceableLoader(
|
||||
ImmutableList.of(loaderWithTextOnly, loaderWithAudioVideoAndText),
|
||||
ImmutableList.of(
|
||||
ImmutableList.of(C.TRACK_TYPE_TEXT),
|
||||
ImmutableList.of(C.TRACK_TYPE_AUDIO, C.TRACK_TYPE_VIDEO, C.TRACK_TYPE_TEXT)));
|
||||
assertThat(compositeSequenceableLoader.getBufferedPositionUs()).isEqualTo(1000);
|
||||
loaderWithAudioVideoAndText.continueLoading(
|
||||
new LoadingInfo.Builder().setPlaybackPositionUs(100).build());
|
||||
assertThat(loaderWithAudioVideoAndText.getBufferedPositionUs()).isEqualTo(C.TIME_END_OF_SOURCE);
|
||||
assertThat(compositeSequenceableLoader.getBufferedPositionUs()).isEqualTo(1000);
|
||||
assertThat(compositeSequenceableLoader.isLoading()).isTrue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that {@link CompositeSequenceableLoader#getBufferedPositionUs()} returns the minimum
|
||||
* buffered position of all loaders if no loader has audio or video tracks.
|
||||
*/
|
||||
@Test
|
||||
public void getBufferedPositionUs_considersAllTracksIfNoneAreAudioOrVideo() {
|
||||
FakeSequenceableLoader loaderWithTextOnly =
|
||||
new FakeSequenceableLoader(/* bufferedPositionUs= */ 999, /* nextLoadPositionUs= */ 2000);
|
||||
FakeSequenceableLoader loaderWithMetadataOnly =
|
||||
new FakeSequenceableLoader(/* bufferedPositionUs= */ 1000, /* nextLoadPositionUs= */ 2000);
|
||||
CompositeSequenceableLoader compositeSequenceableLoader =
|
||||
new CompositeSequenceableLoader(
|
||||
ImmutableList.of(loaderWithTextOnly, loaderWithMetadataOnly),
|
||||
ImmutableList.of(
|
||||
ImmutableList.of(C.TRACK_TYPE_TEXT), ImmutableList.of(C.TRACK_TYPE_METADATA)));
|
||||
assertThat(compositeSequenceableLoader.getBufferedPositionUs()).isEqualTo(999);
|
||||
assertThat(compositeSequenceableLoader.isLoading()).isTrue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that {@link CompositeSequenceableLoader#getNextLoadPositionUs()} returns minimum next
|
||||
* load position among all sub-loaders, and is consistent with {@link
|
||||
@ -100,7 +180,10 @@ public final class CompositeSequenceableLoaderTest {
|
||||
FakeSequenceableLoader loader2 =
|
||||
new FakeSequenceableLoader(/* bufferedPositionUs */ 1001, /* nextLoadPositionUs */ 2000);
|
||||
CompositeSequenceableLoader compositeSequenceableLoader =
|
||||
new CompositeSequenceableLoader(new SequenceableLoader[] {loader1, loader2});
|
||||
new CompositeSequenceableLoader(
|
||||
ImmutableList.of(loader1, loader2),
|
||||
ImmutableList.of(
|
||||
ImmutableList.of(C.TRACK_TYPE_AUDIO), ImmutableList.of(C.TRACK_TYPE_VIDEO)));
|
||||
assertThat(compositeSequenceableLoader.getNextLoadPositionUs()).isEqualTo(2000);
|
||||
assertThat(compositeSequenceableLoader.isLoading()).isTrue();
|
||||
}
|
||||
@ -120,7 +203,12 @@ public final class CompositeSequenceableLoaderTest {
|
||||
new FakeSequenceableLoader(
|
||||
/* bufferedPositionUs */ 1001, /* nextLoadPositionUs */ C.TIME_END_OF_SOURCE);
|
||||
CompositeSequenceableLoader compositeSequenceableLoader =
|
||||
new CompositeSequenceableLoader(new SequenceableLoader[] {loader1, loader2, loader3});
|
||||
new CompositeSequenceableLoader(
|
||||
ImmutableList.of(loader1, loader2, loader3),
|
||||
ImmutableList.of(
|
||||
ImmutableList.of(C.TRACK_TYPE_AUDIO),
|
||||
ImmutableList.of(C.TRACK_TYPE_VIDEO),
|
||||
ImmutableList.of(C.TRACK_TYPE_AUDIO, C.TRACK_TYPE_VIDEO)));
|
||||
assertThat(compositeSequenceableLoader.getNextLoadPositionUs()).isEqualTo(2000);
|
||||
assertThat(compositeSequenceableLoader.isLoading()).isTrue();
|
||||
}
|
||||
@ -139,7 +227,10 @@ public final class CompositeSequenceableLoaderTest {
|
||||
new FakeSequenceableLoader(
|
||||
/* bufferedPositionUs */ 1001, /* nextLoadPositionUs */ C.TIME_END_OF_SOURCE);
|
||||
CompositeSequenceableLoader compositeSequenceableLoader =
|
||||
new CompositeSequenceableLoader(new SequenceableLoader[] {loader1, loader2});
|
||||
new CompositeSequenceableLoader(
|
||||
ImmutableList.of(loader1, loader2),
|
||||
ImmutableList.of(
|
||||
ImmutableList.of(C.TRACK_TYPE_AUDIO), ImmutableList.of(C.TRACK_TYPE_VIDEO)));
|
||||
assertThat(compositeSequenceableLoader.getNextLoadPositionUs()).isEqualTo(C.TIME_END_OF_SOURCE);
|
||||
assertThat(compositeSequenceableLoader.isLoading()).isFalse();
|
||||
}
|
||||
@ -156,7 +247,10 @@ public final class CompositeSequenceableLoaderTest {
|
||||
FakeSequenceableLoader loader2 =
|
||||
new FakeSequenceableLoader(/* bufferedPositionUs */ 1001, /* nextLoadPositionUs */ 2001);
|
||||
CompositeSequenceableLoader compositeSequenceableLoader =
|
||||
new CompositeSequenceableLoader(new SequenceableLoader[] {loader1, loader2});
|
||||
new CompositeSequenceableLoader(
|
||||
ImmutableList.of(loader1, loader2),
|
||||
ImmutableList.of(
|
||||
ImmutableList.of(C.TRACK_TYPE_AUDIO), ImmutableList.of(C.TRACK_TYPE_VIDEO)));
|
||||
compositeSequenceableLoader.continueLoading(
|
||||
new LoadingInfo.Builder().setPlaybackPositionUs(100).build());
|
||||
|
||||
@ -177,7 +271,12 @@ public final class CompositeSequenceableLoaderTest {
|
||||
FakeSequenceableLoader loader3 =
|
||||
new FakeSequenceableLoader(/* bufferedPositionUs */ 1002, /* nextLoadPositionUs */ 2002);
|
||||
CompositeSequenceableLoader compositeSequenceableLoader =
|
||||
new CompositeSequenceableLoader(new SequenceableLoader[] {loader1, loader2, loader3});
|
||||
new CompositeSequenceableLoader(
|
||||
ImmutableList.of(loader1, loader2, loader3),
|
||||
ImmutableList.of(
|
||||
ImmutableList.of(C.TRACK_TYPE_AUDIO),
|
||||
ImmutableList.of(C.TRACK_TYPE_VIDEO),
|
||||
ImmutableList.of(C.TRACK_TYPE_AUDIO, C.TRACK_TYPE_VIDEO)));
|
||||
compositeSequenceableLoader.continueLoading(
|
||||
new LoadingInfo.Builder().setPlaybackPositionUs(3000).build());
|
||||
|
||||
@ -199,7 +298,10 @@ public final class CompositeSequenceableLoaderTest {
|
||||
new FakeSequenceableLoader(
|
||||
/* bufferedPositionUs */ 1001, /* nextLoadPositionUs */ C.TIME_END_OF_SOURCE);
|
||||
CompositeSequenceableLoader compositeSequenceableLoader =
|
||||
new CompositeSequenceableLoader(new SequenceableLoader[] {loader1, loader2});
|
||||
new CompositeSequenceableLoader(
|
||||
ImmutableList.of(loader1, loader2),
|
||||
ImmutableList.of(
|
||||
ImmutableList.of(C.TRACK_TYPE_AUDIO), ImmutableList.of(C.TRACK_TYPE_VIDEO)));
|
||||
compositeSequenceableLoader.continueLoading(
|
||||
new LoadingInfo.Builder().setPlaybackPositionUs(3000).build());
|
||||
|
||||
@ -221,7 +323,10 @@ public final class CompositeSequenceableLoaderTest {
|
||||
loader1.setNextChunkDurationUs(1000);
|
||||
|
||||
CompositeSequenceableLoader compositeSequenceableLoader =
|
||||
new CompositeSequenceableLoader(new SequenceableLoader[] {loader1, loader2});
|
||||
new CompositeSequenceableLoader(
|
||||
ImmutableList.of(loader1, loader2),
|
||||
ImmutableList.of(
|
||||
ImmutableList.of(C.TRACK_TYPE_AUDIO), ImmutableList.of(C.TRACK_TYPE_VIDEO)));
|
||||
|
||||
assertThat(
|
||||
compositeSequenceableLoader.continueLoading(
|
||||
@ -244,7 +349,10 @@ public final class CompositeSequenceableLoaderTest {
|
||||
loader2.setNextChunkDurationUs(1000);
|
||||
|
||||
CompositeSequenceableLoader compositeSequenceableLoader =
|
||||
new CompositeSequenceableLoader(new SequenceableLoader[] {loader1, loader2});
|
||||
new CompositeSequenceableLoader(
|
||||
ImmutableList.of(loader1, loader2),
|
||||
ImmutableList.of(
|
||||
ImmutableList.of(C.TRACK_TYPE_AUDIO), ImmutableList.of(C.TRACK_TYPE_VIDEO)));
|
||||
|
||||
assertThat(
|
||||
compositeSequenceableLoader.continueLoading(
|
||||
|
@ -59,6 +59,7 @@ import androidx.media3.exoplayer.upstream.CmcdConfiguration;
|
||||
import androidx.media3.exoplayer.upstream.LoadErrorHandlingPolicy;
|
||||
import androidx.media3.exoplayer.upstream.LoaderErrorThrower;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.primitives.Ints;
|
||||
import java.io.IOException;
|
||||
@ -152,8 +153,7 @@ import java.util.regex.Pattern;
|
||||
sampleStreams = newSampleStreamArray(0);
|
||||
eventSampleStreams = new EventSampleStream[0];
|
||||
trackEmsgHandlerBySampleStream = new IdentityHashMap<>();
|
||||
compositeSequenceableLoader =
|
||||
compositeSequenceableLoaderFactory.createCompositeSequenceableLoader(sampleStreams);
|
||||
compositeSequenceableLoader = compositeSequenceableLoaderFactory.empty();
|
||||
Period period = manifest.getPeriod(periodIndex);
|
||||
eventStreams = period.eventStreams;
|
||||
Pair<TrackGroupArray, TrackGroupInfo[]> result =
|
||||
@ -302,7 +302,9 @@ import java.util.regex.Pattern;
|
||||
eventSampleStreamList.toArray(eventSampleStreams);
|
||||
|
||||
compositeSequenceableLoader =
|
||||
compositeSequenceableLoaderFactory.createCompositeSequenceableLoader(sampleStreams);
|
||||
compositeSequenceableLoaderFactory.create(
|
||||
sampleStreamList,
|
||||
Lists.transform(sampleStreamList, s -> ImmutableList.of(s.primaryTrackType)));
|
||||
return positionUs;
|
||||
}
|
||||
|
||||
|
@ -51,6 +51,8 @@ import androidx.media3.exoplayer.upstream.Allocator;
|
||||
import androidx.media3.exoplayer.upstream.CmcdConfiguration;
|
||||
import androidx.media3.exoplayer.upstream.LoadErrorHandlingPolicy;
|
||||
import androidx.media3.extractor.Extractor;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.primitives.Ints;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
@ -157,8 +159,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
this.playerId = playerId;
|
||||
this.timestampAdjusterInitializationTimeoutMs = timestampAdjusterInitializationTimeoutMs;
|
||||
sampleStreamWrapperCallback = new SampleStreamWrapperCallback();
|
||||
compositeSequenceableLoader =
|
||||
compositeSequenceableLoaderFactory.createCompositeSequenceableLoader();
|
||||
compositeSequenceableLoader = compositeSequenceableLoaderFactory.empty();
|
||||
streamWrapperIndices = new IdentityHashMap<>();
|
||||
timestampAdjusterProvider = new TimestampAdjusterProvider();
|
||||
sampleStreamWrappers = new HlsSampleStreamWrapper[0];
|
||||
@ -372,9 +373,14 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
// Update the local state.
|
||||
enabledSampleStreamWrappers =
|
||||
Util.nullSafeArrayCopy(newEnabledSampleStreamWrappers, newEnabledSampleStreamWrapperCount);
|
||||
ImmutableList<HlsSampleStreamWrapper> enabledSampleStreamWrappersList =
|
||||
ImmutableList.copyOf(enabledSampleStreamWrappers);
|
||||
compositeSequenceableLoader =
|
||||
compositeSequenceableLoaderFactory.createCompositeSequenceableLoader(
|
||||
enabledSampleStreamWrappers);
|
||||
compositeSequenceableLoaderFactory.create(
|
||||
enabledSampleStreamWrappersList,
|
||||
Lists.transform(
|
||||
enabledSampleStreamWrappersList,
|
||||
sampleStreamWrapper -> sampleStreamWrapper.getTrackGroups().getTrackTypes()));
|
||||
return positionUs;
|
||||
}
|
||||
|
||||
|
@ -41,6 +41,8 @@ import androidx.media3.exoplayer.upstream.Allocator;
|
||||
import androidx.media3.exoplayer.upstream.CmcdConfiguration;
|
||||
import androidx.media3.exoplayer.upstream.LoadErrorHandlingPolicy;
|
||||
import androidx.media3.exoplayer.upstream.LoaderErrorThrower;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Lists;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@ -91,8 +93,7 @@ import java.util.List;
|
||||
this.compositeSequenceableLoaderFactory = compositeSequenceableLoaderFactory;
|
||||
trackGroups = buildTrackGroups(manifest, drmSessionManager, chunkSourceFactory);
|
||||
sampleStreams = newSampleStreamArray(0);
|
||||
compositeSequenceableLoader =
|
||||
compositeSequenceableLoaderFactory.createCompositeSequenceableLoader(sampleStreams);
|
||||
compositeSequenceableLoader = compositeSequenceableLoaderFactory.empty();
|
||||
}
|
||||
|
||||
public void updateManifest(SsManifest manifest) {
|
||||
@ -158,7 +159,9 @@ import java.util.List;
|
||||
sampleStreams = newSampleStreamArray(sampleStreamsList.size());
|
||||
sampleStreamsList.toArray(sampleStreams);
|
||||
compositeSequenceableLoader =
|
||||
compositeSequenceableLoaderFactory.createCompositeSequenceableLoader(sampleStreams);
|
||||
compositeSequenceableLoaderFactory.create(
|
||||
sampleStreamsList,
|
||||
Lists.transform(sampleStreamsList, s -> ImmutableList.of(s.primaryTrackType)));
|
||||
return positionUs;
|
||||
}
|
||||
|
||||
|
@ -44,7 +44,9 @@ import androidx.media3.exoplayer.source.chunk.ChunkSampleStream;
|
||||
import androidx.media3.exoplayer.trackselection.ExoTrackSelection;
|
||||
import androidx.media3.exoplayer.upstream.Allocator;
|
||||
import androidx.media3.exoplayer.upstream.DefaultLoadErrorHandlingPolicy;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.Lists;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@ -86,7 +88,9 @@ public class FakeAdaptiveMediaPeriod
|
||||
this.durationUs = durationUs;
|
||||
this.transferListener = transferListener;
|
||||
sampleStreams = new ArrayList<>();
|
||||
sequenceableLoader = new CompositeSequenceableLoader(new SequenceableLoader[0]);
|
||||
sequenceableLoader =
|
||||
new CompositeSequenceableLoader(
|
||||
/* loaders= */ ImmutableList.of(), /* loaderTrackTypes= */ ImmutableList.of());
|
||||
fakePreparationLoadTaskId = LoadEventInfo.getNewId();
|
||||
}
|
||||
|
||||
@ -97,7 +101,9 @@ public class FakeAdaptiveMediaPeriod
|
||||
sampleStream.release();
|
||||
}
|
||||
sampleStreams.clear();
|
||||
sequenceableLoader = new CompositeSequenceableLoader(new SequenceableLoader[0]);
|
||||
sequenceableLoader =
|
||||
new CompositeSequenceableLoader(
|
||||
/* loaders= */ ImmutableList.of(), /* loaderTrackTypes= */ ImmutableList.of());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -143,7 +149,7 @@ public class FakeAdaptiveMediaPeriod
|
||||
return trackGroupArray;
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"}) // Casting sample streams created by this class.
|
||||
@SuppressWarnings({"unchecked"}) // Casting sample streams created by this class.
|
||||
@Override
|
||||
public long selectTracks(
|
||||
@NullableType ExoTrackSelection[] selections,
|
||||
@ -188,7 +194,9 @@ public class FakeAdaptiveMediaPeriod
|
||||
}
|
||||
}
|
||||
sequenceableLoader =
|
||||
new CompositeSequenceableLoader(sampleStreams.toArray(new ChunkSampleStream[0]));
|
||||
new CompositeSequenceableLoader(
|
||||
sampleStreams,
|
||||
Lists.transform(sampleStreams, s -> ImmutableList.of(s.primaryTrackType)));
|
||||
return seekToUs(positionUs);
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user