Skip to content

Commit

Permalink
CompositeSequenceableLoader: Prefer buffered position of A/V tracks
Browse files Browse the repository at this point in the history
Preferring audio and video tracks matches logic we already have in
[`ProgressiveMediaPeriod.getBufferedPositionUs`](https://github.com/androidx/media/blob/f6fe90f30ba022c7e04e14a3dc5c28c568a4d1e2/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/ProgressiveMediaPeriod.java#L403).

PiperOrigin-RevId: 599154217
  • Loading branch information
icbaker authored and Copybara-Service committed Jan 17, 2024
1 parent 80bfa81 commit 406c0a1
Show file tree
Hide file tree
Showing 10 changed files with 286 additions and 42 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,39 +15,80 @@
*/
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;

/**
* @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();
}

@Override
public long getBufferedPositionUs() {
long bufferedPositionUs = Long.MAX_VALUE;
for (SequenceableLoader loader : loaders) {
boolean hasAudioVideoTracks = false;
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)) {
hasAudioVideoTracks = true;
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 (hasAudioVideoTracks) {
return bufferedPositionAudioVideoUs != Long.MAX_VALUE
? bufferedPositionAudioVideoUs
: C.TIME_END_OF_SOURCE;
} else {
return bufferedPositionUs != Long.MAX_VALUE ? bufferedPositionUs : C.TIME_END_OF_SOURCE;
}
}

@Override
public long getNextLoadPositionUs() {
long nextLoadPositionUs = Long.MAX_VALUE;
for (SequenceableLoader loader : loaders) {
long loaderNextLoadPositionUs = loader.getNextLoadPositionUs();
for (int i = 0; i < loadersWithTrackTypes.size(); i++) {
long loaderNextLoadPositionUs = loadersWithTrackTypes.get(i).getNextLoadPositionUs();
if (loaderNextLoadPositionUs != C.TIME_END_OF_SOURCE) {
nextLoadPositionUs = min(nextLoadPositionUs, loaderNextLoadPositionUs);
}
Expand All @@ -57,8 +98,8 @@ public long getNextLoadPositionUs() {

@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);
}
}

Expand All @@ -72,13 +113,13 @@ public boolean continueLoading(LoadingInfo loadingInfo) {
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;
Expand All @@ -88,11 +129,54 @@ public boolean continueLoading(LoadingInfo loadingInfo) {

@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);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,32 @@
*/
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
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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
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;
Expand Down Expand Up @@ -58,8 +59,7 @@ public MergingMediaPeriod(
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++) {
Expand Down Expand Up @@ -172,7 +172,9 @@ public long selectTracks(
// 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;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
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;

/**
Expand Down Expand Up @@ -90,6 +91,11 @@ public boolean isEmpty() {
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) {
Expand Down
Loading

0 comments on commit 406c0a1

Please sign in to comment.