diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 95c098e151..ad2956cb4d 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -5,10 +5,13 @@ * Support for playing spherical videos on Daydream. * Improve decoder re-use between playbacks. TODO: Write and link a blog post here ([#2826](https://github.com/google/ExoPlayer/issues/2826)). -* Add options for controlling audio track selections to `DefaultTrackSelector` - ([#3314](https://github.com/google/ExoPlayer/issues/3314)). +* Track selection: + * Add options for controlling audio track selections to `DefaultTrackSelector` + ([#3314](https://github.com/google/ExoPlayer/issues/3314)). + * Update `TrackSelection.Factory` interface to support creating all track + selections together. * Do not retry failed loads whose error is `FileNotFoundException`. -* Prevent Cea608Decoder from generating Subtitles with null Cues list +* Prevent Cea608Decoder from generating Subtitles with null Cues list. * Caching: Cache data with unknown length by default. The previous flag to opt in to this behavior (`DataSpec.FLAG_ALLOW_CACHING_UNKNOWN_LENGTH`) has been replaced with an opt out flag (`DataSpec.FLAG_DONT_CACHE_IF_LENGTH_UNKNOWN`). diff --git a/library/core/src/main/java/com/google/android/exoplayer2/trackselection/AdaptiveTrackSelection.java b/library/core/src/main/java/com/google/android/exoplayer2/trackselection/AdaptiveTrackSelection.java index 66b49555ef..b39a5d19f0 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/trackselection/AdaptiveTrackSelection.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/trackselection/AdaptiveTrackSelection.java @@ -227,8 +227,36 @@ public class AdaptiveTrackSelection extends BaseTrackSelection { } @Override - public AdaptiveTrackSelection createTrackSelection( - TrackGroup group, BandwidthMeter bandwidthMeter, int... tracks) { + public @NullableType TrackSelection[] createTrackSelections( + @NullableType Definition[] definitions, BandwidthMeter bandwidthMeter) { + TrackSelection[] selections = new TrackSelection[definitions.length]; + AdaptiveTrackSelection adaptiveSelection = null; + int totalFixedBandwidth = 0; + for (int i = 0; i < definitions.length; i++) { + Definition definition = definitions[i]; + if (definition == null) { + continue; + } + if (definition.tracks.length > 1) { + adaptiveSelection = + createAdaptiveTrackSelection(definition.group, bandwidthMeter, definition.tracks); + selections[i] = adaptiveSelection; + } else { + selections[i] = new FixedTrackSelection(definition.group, definition.tracks[0]); + int trackBitrate = definition.group.getFormat(definition.tracks[0]).bitrate; + if (trackBitrate != Format.NO_VALUE) { + totalFixedBandwidth += trackBitrate; + } + } + } + if (blockFixedTrackSelectionBandwidth && adaptiveSelection != null) { + adaptiveSelection.experimental_setNonAllocatableBandwidth(totalFixedBandwidth); + } + return selections; + } + + private AdaptiveTrackSelection createAdaptiveTrackSelection( + TrackGroup group, BandwidthMeter bandwidthMeter, int[] tracks) { if (this.bandwidthMeter != null) { bandwidthMeter = this.bandwidthMeter; } @@ -246,34 +274,6 @@ public class AdaptiveTrackSelection extends BaseTrackSelection { adaptiveTrackSelection.experimental_setTrackBitrateEstimator(trackBitrateEstimator); return adaptiveTrackSelection; } - - @Override - public @NullableType TrackSelection[] createTrackSelections( - @NullableType Definition[] definitions, BandwidthMeter bandwidthMeter) { - TrackSelection[] selections = new TrackSelection[definitions.length]; - AdaptiveTrackSelection adaptiveSelection = null; - int totalFixedBandwidth = 0; - for (int i = 0; i < definitions.length; i++) { - Definition definition = definitions[i]; - if (definition == null) { - continue; - } - if (definition.tracks.length > 1) { - selections[i] = createTrackSelection(definition.group, bandwidthMeter, definition.tracks); - adaptiveSelection = (AdaptiveTrackSelection) selections[i]; - } else { - selections[i] = new FixedTrackSelection(definition.group, definition.tracks[0]); - int trackBitrate = definition.group.getFormat(definition.tracks[0]).bitrate; - if (trackBitrate != Format.NO_VALUE) { - totalFixedBandwidth += trackBitrate; - } - } - } - if (blockFixedTrackSelectionBandwidth && adaptiveSelection != null) { - adaptiveSelection.experimental_setNonAllocatableBandwidth(totalFixedBandwidth); - } - return selections; - } } public static final int DEFAULT_MIN_DURATION_FOR_QUALITY_INCREASE_MS = 10000; diff --git a/library/core/src/main/java/com/google/android/exoplayer2/trackselection/BufferSizeAdaptationBuilder.java b/library/core/src/main/java/com/google/android/exoplayer2/trackselection/BufferSizeAdaptationBuilder.java index 6239dd04ad..5c8350cb1d 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/trackselection/BufferSizeAdaptationBuilder.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/trackselection/BufferSizeAdaptationBuilder.java @@ -24,12 +24,14 @@ import com.google.android.exoplayer2.LoadControl; import com.google.android.exoplayer2.source.TrackGroup; import com.google.android.exoplayer2.source.chunk.MediaChunk; import com.google.android.exoplayer2.source.chunk.MediaChunkIterator; +import com.google.android.exoplayer2.trackselection.TrackSelection.Definition; import com.google.android.exoplayer2.upstream.BandwidthMeter; import com.google.android.exoplayer2.upstream.DefaultAllocator; import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Clock; import com.google.android.exoplayer2.util.PriorityTaskManager; import java.util.List; +import org.checkerframework.checker.nullness.compatqual.NullableType; /** * Builder for a {@link TrackSelection.Factory} and {@link LoadControl} that implement buffer size @@ -273,19 +275,22 @@ public final class BufferSizeAdaptationBuilder { TrackSelection.Factory trackSelectionFactory = new TrackSelection.Factory() { @Override - public TrackSelection createTrackSelection( - TrackGroup group, BandwidthMeter bandwidthMeter, int... tracks) { - return new BufferSizeAdaptiveTrackSelection( - group, - tracks, - bandwidthMeter, - minBufferMs, - maxBufferMs, - hysteresisBufferMs, - startUpBandwidthFraction, - startUpMinBufferForQualityIncreaseMs, - dynamicFormatFilter, - clock); + public @NullableType TrackSelection[] createTrackSelections( + @NullableType Definition[] definitions, BandwidthMeter bandwidthMeter) { + return TrackSelectionUtil.createTrackSelectionsForDefinitions( + definitions, + definition -> + new BufferSizeAdaptiveTrackSelection( + definition.group, + definition.tracks, + bandwidthMeter, + minBufferMs, + maxBufferMs, + hysteresisBufferMs, + startUpBandwidthFraction, + startUpMinBufferForQualityIncreaseMs, + dynamicFormatFilter, + clock)); } }; diff --git a/library/core/src/main/java/com/google/android/exoplayer2/trackselection/FixedTrackSelection.java b/library/core/src/main/java/com/google/android/exoplayer2/trackselection/FixedTrackSelection.java index 7755e437ce..79b5d93dc7 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/trackselection/FixedTrackSelection.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/trackselection/FixedTrackSelection.java @@ -21,8 +21,8 @@ import com.google.android.exoplayer2.source.TrackGroup; import com.google.android.exoplayer2.source.chunk.MediaChunk; import com.google.android.exoplayer2.source.chunk.MediaChunkIterator; import com.google.android.exoplayer2.upstream.BandwidthMeter; -import com.google.android.exoplayer2.util.Assertions; import java.util.List; +import org.checkerframework.checker.nullness.compatqual.NullableType; /** * A {@link TrackSelection} consisting of a single track. @@ -56,10 +56,12 @@ public final class FixedTrackSelection extends BaseTrackSelection { } @Override - public FixedTrackSelection createTrackSelection( - TrackGroup group, BandwidthMeter bandwidthMeter, int... tracks) { - Assertions.checkArgument(tracks.length == 1); - return new FixedTrackSelection(group, tracks[0], reason, data); + public @NullableType TrackSelection[] createTrackSelections( + @NullableType Definition[] definitions, BandwidthMeter bandwidthMeter) { + return TrackSelectionUtil.createTrackSelectionsForDefinitions( + definitions, + definition -> + new FixedTrackSelection(definition.group, definition.tracks[0], reason, data)); } } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/trackselection/RandomTrackSelection.java b/library/core/src/main/java/com/google/android/exoplayer2/trackselection/RandomTrackSelection.java index e3c643670b..217a16e4a6 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/trackselection/RandomTrackSelection.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/trackselection/RandomTrackSelection.java @@ -24,6 +24,7 @@ import com.google.android.exoplayer2.source.chunk.MediaChunkIterator; import com.google.android.exoplayer2.upstream.BandwidthMeter; import java.util.List; import java.util.Random; +import org.checkerframework.checker.nullness.compatqual.NullableType; /** * A {@link TrackSelection} whose selected track is updated randomly. @@ -49,9 +50,11 @@ public final class RandomTrackSelection extends BaseTrackSelection { } @Override - public RandomTrackSelection createTrackSelection( - TrackGroup group, BandwidthMeter bandwidthMeter, int... tracks) { - return new RandomTrackSelection(group, tracks, random); + public @NullableType TrackSelection[] createTrackSelections( + @NullableType Definition[] definitions, BandwidthMeter bandwidthMeter) { + return TrackSelectionUtil.createTrackSelectionsForDefinitions( + definitions, + definition -> new RandomTrackSelection(definition.group, definition.tracks, random)); } } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/trackselection/TrackSelection.java b/library/core/src/main/java/com/google/android/exoplayer2/trackselection/TrackSelection.java index 13e823da29..251c0ac76b 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/trackselection/TrackSelection.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/trackselection/TrackSelection.java @@ -21,8 +21,8 @@ import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.source.TrackGroup; import com.google.android.exoplayer2.source.chunk.MediaChunk; import com.google.android.exoplayer2.source.chunk.MediaChunkIterator; +import com.google.android.exoplayer2.trackselection.TrackSelectionUtil.AdaptiveTrackSelectionFactory; import com.google.android.exoplayer2.upstream.BandwidthMeter; -import com.google.android.exoplayer2.util.Assertions; import java.util.List; import org.checkerframework.checker.nullness.compatqual.NullableType; @@ -61,42 +61,31 @@ public interface TrackSelection { interface Factory { /** - * Creates a new selection. - * - * @param group The {@link TrackGroup}. Must not be null. - * @param bandwidthMeter A {@link BandwidthMeter} which can be used to select tracks. - * @param tracks The indices of the selected tracks within the {@link TrackGroup}. Must not be - * null or empty. May be in any order. - * @return The created selection. + * @deprecated Implement {@link #createTrackSelections(Definition[], BandwidthMeter)} instead. + * Calling {@link TrackSelectionUtil#createTrackSelectionsForDefinitions(Definition[], + * AdaptiveTrackSelectionFactory)} helps to create a single adaptive track selection in the + * same way as using this deprecated method. */ - TrackSelection createTrackSelection( - TrackGroup group, BandwidthMeter bandwidthMeter, int... tracks); + @Deprecated + default TrackSelection createTrackSelection( + TrackGroup group, BandwidthMeter bandwidthMeter, int... tracks) { + throw new UnsupportedOperationException(); + } /** * Creates a new selection for each {@link Definition}. * * @param definitions A {@link Definition} array. May include null values. * @param bandwidthMeter A {@link BandwidthMeter} which can be used to select tracks. - * @return The created selections. For null entries in {@code definitions} returns null values. + * @return The created selections. Must have the same length as {@code definitions} and may + * include null values. */ + @SuppressWarnings("deprecation") default @NullableType TrackSelection[] createTrackSelections( @NullableType Definition[] definitions, BandwidthMeter bandwidthMeter) { - TrackSelection[] selections = new TrackSelection[definitions.length]; - boolean createdAdaptiveTrackSelection = false; - for (int i = 0; i < definitions.length; i++) { - Definition definition = definitions[i]; - if (definition == null) { - continue; - } - if (definition.tracks.length > 1) { - Assertions.checkState(!createdAdaptiveTrackSelection); - createdAdaptiveTrackSelection = true; - selections[i] = createTrackSelection(definition.group, bandwidthMeter, definition.tracks); - } else { - selections[i] = new FixedTrackSelection(definition.group, definition.tracks[0]); - } - } - return selections; + return TrackSelectionUtil.createTrackSelectionsForDefinitions( + definitions, + definition -> createTrackSelection(definition.group, bandwidthMeter, definition.tracks)); } } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/trackselection/TrackSelectionUtil.java b/library/core/src/main/java/com/google/android/exoplayer2/trackselection/TrackSelectionUtil.java index 947f64be2c..7800495a62 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/trackselection/TrackSelectionUtil.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/trackselection/TrackSelectionUtil.java @@ -22,15 +22,59 @@ import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.source.chunk.MediaChunk; import com.google.android.exoplayer2.source.chunk.MediaChunkIterator; import com.google.android.exoplayer2.source.chunk.MediaChunkListIterator; +import com.google.android.exoplayer2.trackselection.TrackSelection.Definition; import com.google.android.exoplayer2.util.Assertions; import java.util.Arrays; import java.util.List; +import org.checkerframework.checker.nullness.compatqual.NullableType; /** Track selection related utility methods. */ public final class TrackSelectionUtil { private TrackSelectionUtil() {} + /** Functional interface to create a single adaptive track selection. */ + public interface AdaptiveTrackSelectionFactory { + + /** + * Creates an adaptive track selection for the provided track selection definition. + * + * @param trackSelectionDefinition A {@link Definition} for the track selection. + * @return The created track selection. + */ + TrackSelection createAdaptiveTrackSelection(Definition trackSelectionDefinition); + } + + /** + * Creates track selections for an array of track selection definitions, with at most one + * multi-track adaptive selection. + * + * @param definitions The list of track selection {@link Definition definitions}. May include null + * values. + * @param adaptiveTrackSelectionFactory A factory for the multi-track adaptive track selection. + * @return The array of created track selection. For null entries in {@code definitions} returns + * null values. + */ + public static @NullableType TrackSelection[] createTrackSelectionsForDefinitions( + @NullableType Definition[] definitions, + AdaptiveTrackSelectionFactory adaptiveTrackSelectionFactory) { + TrackSelection[] selections = new TrackSelection[definitions.length]; + boolean createdAdaptiveTrackSelection = false; + for (int i = 0; i < definitions.length; i++) { + Definition definition = definitions[i]; + if (definition == null) { + continue; + } + if (definition.tracks.length > 1 && !createdAdaptiveTrackSelection) { + createdAdaptiveTrackSelection = true; + selections[i] = adaptiveTrackSelectionFactory.createAdaptiveTrackSelection(definition); + } else { + selections[i] = new FixedTrackSelection(definition.group, definition.tracks[0]); + } + } + return selections; + } + /** * Returns average bitrate for chunks in bits per second. Chunks are included in average until * {@code maxDurationMs} or the first unknown length chunk. diff --git a/library/core/src/test/java/com/google/android/exoplayer2/trackselection/AdaptiveTrackSelectionTest.java b/library/core/src/test/java/com/google/android/exoplayer2/trackselection/AdaptiveTrackSelectionTest.java index 29018a520c..c3836e63f6 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/trackselection/AdaptiveTrackSelectionTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/trackselection/AdaptiveTrackSelectionTest.java @@ -32,6 +32,7 @@ import com.google.android.exoplayer2.source.TrackGroup; import com.google.android.exoplayer2.source.chunk.MediaChunkIterator; import com.google.android.exoplayer2.testutil.FakeClock; import com.google.android.exoplayer2.testutil.FakeMediaChunk; +import com.google.android.exoplayer2.trackselection.TrackSelection.Definition; import com.google.android.exoplayer2.upstream.BandwidthMeter; import com.google.android.exoplayer2.util.MimeTypes; import java.util.ArrayList; @@ -66,15 +67,20 @@ public final class AdaptiveTrackSelectionTest { } @Test + @SuppressWarnings("deprecation") public void testFactoryUsesInitiallyProvidedBandwidthMeter() { BandwidthMeter initialBandwidthMeter = mock(BandwidthMeter.class); BandwidthMeter injectedBandwidthMeter = mock(BandwidthMeter.class); - Format format = videoFormat(/* bitrate= */ 500, /* width= */ 320, /* height= */ 240); - @SuppressWarnings("deprecation") - AdaptiveTrackSelection adaptiveTrackSelection = + Format format1 = videoFormat(/* bitrate= */ 500, /* width= */ 320, /* height= */ 240); + Format format2 = videoFormat(/* bitrate= */ 1000, /* width= */ 640, /* height= */ 480); + TrackSelection[] trackSelections = new AdaptiveTrackSelection.Factory(initialBandwidthMeter) - .createTrackSelection(new TrackGroup(format), injectedBandwidthMeter, /* tracks= */ 0); - adaptiveTrackSelection.updateSelectedTrack( + .createTrackSelections( + new Definition[] { + new Definition(new TrackGroup(format1, format2), /* tracks= */ 0, 1) + }, + injectedBandwidthMeter); + trackSelections[0].updateSelectedTrack( /* playbackPositionUs= */ 0, /* bufferedDurationUs= */ 0, /* availableDurationUs= */ C.TIME_UNSET, diff --git a/testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/FakeTrackSelector.java b/testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/FakeTrackSelector.java index 6d37961005..ac39ba8de6 100644 --- a/testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/FakeTrackSelector.java +++ b/testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/FakeTrackSelector.java @@ -79,8 +79,19 @@ public class FakeTrackSelector extends DefaultTrackSelector { } @Override - public TrackSelection createTrackSelection( - TrackGroup trackGroup, BandwidthMeter bandwidthMeter, int... tracks) { + public TrackSelection[] createTrackSelections( + TrackSelection.Definition[] definitions, BandwidthMeter bandwidthMeter) { + TrackSelection[] selections = new TrackSelection[definitions.length]; + for (int i = 0; i < definitions.length; i++) { + TrackSelection.Definition definition = definitions[i]; + if (definition != null) { + selections[i] = createTrackSelection(definition.group); + } + } + return selections; + } + + private TrackSelection createTrackSelection(TrackGroup trackGroup) { if (mayReuseTrackSelection) { for (FakeTrackSelection trackSelection : trackSelections) { if (trackSelection.getTrackGroup().equals(trackGroup)) { @@ -92,18 +103,5 @@ public class FakeTrackSelector extends DefaultTrackSelector { trackSelections.add(trackSelection); return trackSelection; } - - @Override - public TrackSelection[] createTrackSelections( - TrackSelection.Definition[] definitions, BandwidthMeter bandwidthMeter) { - TrackSelection[] selections = new TrackSelection[definitions.length]; - for (int i = 0; i < definitions.length; i++) { - TrackSelection.Definition definition = definitions[i]; - if (definition != null) { - selections[i] = createTrackSelection(definition.group, bandwidthMeter, definition.tracks); - } - } - return selections; - } } }