Encapsulate TrackSelectionOverrides in its own class

The current API exposes an `ImmutableMap` of
`TrackGroup` -> `TrackSelectionOverride`.
This has several disadvantages:
 - A difficult to use API for mutation
   (`ImmutableMap.Builder` doesn't support key removal).
 - There is no track selection specific methods,
   how the generic map API mapps to the selection override is not complex
   but to obvious for a casual reader.
 - The internal data type is exposed, making internal refactor difficult.

This was done to have the API ready as quick as possible.

When transitioning the clients to the map API in <unknown commit>,
it became clear that the map API was too verbose and not mapping
to the clients needs, so utility methods
were added to make operations clearer and more concise.

Nevertheless, having to use utility method to use easily and correctly
an API is not the sign of a good API.

This cl refactors the track selection API for several improvements:

 - Add a type `TrackSelectionParameters` that encapsulate the internal
   data structure (map currently).
 - For iteration, expose as a list.
 - Add a `Builder` for easy mutable operations.
 - Add track selection specific methods to avoid having utilities functions.
 - Those operations are the same as `DefaultTrackSelector.Parameters`
   for easier migration. (`setOverride` was renamed to `addOverride`)
 - Move `TrackSelection` classes outside of `TrackSelectionParameters`
   as their own top level classes.

The migration of the client code is straightforward as most of it
were already using the previously mentioned utility functions
that are now native methods.

The full migration has not been done yet, and is pending on this cl approval.

PiperOrigin-RevId: 405362719
This commit is contained in:
krocard 2021-10-25 11:50:47 +01:00 committed by Oliver Woodman
parent 2ee72076e5
commit 1f3f22a709
8 changed files with 614 additions and 251 deletions

View File

@ -0,0 +1,301 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.exoplayer2.trackselection;
import static com.google.android.exoplayer2.util.Assertions.checkNotNull;
import static com.google.android.exoplayer2.util.BundleableUtil.fromBundleNullableList;
import static com.google.android.exoplayer2.util.BundleableUtil.toBundleArrayList;
import static java.util.Collections.max;
import static java.util.Collections.min;
import android.os.Bundle;
import androidx.annotation.IntDef;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.trackselection.C.TrackType;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.primitives.Ints;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
/**
* Forces the selection of the specified tracks in {@link TrackGroup TrackGroups}.
*
* <p>Each {@link TrackSelectionOverride override} only affects the selection of tracks of that
* {@link TrackType type}. For example overriding the selection of an {@link C#TRACK_TYPE_AUDIO
* audio} {@link TrackGroup} will not affect the selection of {@link C#TRACK_TYPE_VIDEO video} or
* {@link C#TRACK_TYPE_TEXT text} tracks.
*
* <p>If multiple {@link TrackGroup TrackGroups} of the same {@link TrackType} are overridden, which
* tracks will be selected depend on the player capabilities. For example, by default {@code
* ExoPlayer} doesn't support selecting more than one {@link TrackGroup} per {@link TrackType}.
*
* <p>Overrides of {@link TrackGroup} that are not currently available are ignored. For example,
* when the player transitions to the next {@link MediaItem} in a playlist, any overrides of the
* previous {@link MediaItem} are ignored.
*
* @see TrackSelectionParameters#trackSelectionOverrides
*/
public final class TrackSelectionOverrides implements Bundleable {
/** Builder for {@link TrackSelectionOverrides}. */
public static final class Builder {
// Cannot use ImmutableMap.Builder as it doesn't support removing entries.
private final HashMap<TrackGroup, TrackSelectionOverride> overrides;
/** Creates an builder with no {@link TrackSelectionOverride}. */
public Builder() {
overrides = new HashMap<>();
}
private Builder(Map<TrackGroup, TrackSelectionOverride> overrides) {
this.overrides = new HashMap<>(overrides);
}
/** Adds an override for the provided {@link TrackGroup}. */
public Builder addOverride(TrackSelectionOverride override) {
overrides.put(override.trackGroup, override);
return this;
}
/** Removes the override associated with the provided {@link TrackGroup} if present. */
public Builder clearOverride(TrackGroup trackGroup) {
overrides.remove(trackGroup);
return this;
}
/** Set the override for the type of the provided {@link TrackGroup}. */
public Builder setOverrideForType(TrackSelectionOverride override) {
clearOverridesOfType(override.getTrackType());
overrides.put(override.trackGroup, override);
return this;
}
/**
* Remove any override associated with {@link TrackGroup TrackGroups} of type {@code trackType}.
*/
public Builder clearOverridesOfType(@TrackType int trackType) {
for (Iterator<TrackSelectionOverride> it = overrides.values().iterator(); it.hasNext(); ) {
TrackSelectionOverride trackSelectionOverride = it.next();
if (trackSelectionOverride.getTrackType() == trackType) {
it.remove();
}
}
return this;
}
/** Returns a new {@link TrackSelectionOverrides} instance with the current builder values. */
public TrackSelectionOverrides build() {
return new TrackSelectionOverrides(overrides);
}
}
/**
* Forces the selection of {@link #trackIndexes} for a {@link TrackGroup}.
*
* <p>If multiple {link #tracks} are overridden, as many as possible will be selected depending on
* the player capabilities.
*
* <p>If a {@link TrackSelectionOverride} has no tracks ({@code tracks.isEmpty()}), no tracks will
* be played. This is similar to {@link TrackSelectionParameters#disabledTrackTypes}, except it
* will only affect the playback of the associated {@link TrackGroup}. For example, if the only
* {@link C#TRACK_TYPE_VIDEO} {@link TrackGroup} is associated with no tracks, no video will play
* until the next video starts.
*/
public static final class TrackSelectionOverride implements Bundleable {
/** The {@link TrackGroup} whose {@link #trackIndexes} are forced to be selected. */
public final TrackGroup trackGroup;
/** The index of tracks in a {@link TrackGroup} to be selected. */
public final ImmutableList<Integer> trackIndexes;
/** Constructs an instance to force all tracks in {@code trackGroup} to be selected. */
public TrackSelectionOverride(TrackGroup trackGroup) {
this.trackGroup = trackGroup;
ImmutableList.Builder<Integer> builder = new ImmutableList.Builder<>();
for (int i = 0; i < trackGroup.length; i++) {
builder.add(i);
}
this.trackIndexes = builder.build();
}
/**
* Constructs an instance to force {@code trackIndexes} in {@code trackGroup} to be selected.
*
* @param trackGroup The {@link TrackGroup} for which to override the track selection.
* @param trackIndexes The indexes of the tracks in the {@link TrackGroup} to select.
*/
public TrackSelectionOverride(TrackGroup trackGroup, List<Integer> trackIndexes) {
if (!trackIndexes.isEmpty()) {
if (min(trackIndexes) < 0 || max(trackIndexes) >= trackGroup.length) {
throw new IndexOutOfBoundsException();
}
}
this.trackGroup = trackGroup;
this.trackIndexes = ImmutableList.copyOf(trackIndexes);
}
@Override
public boolean equals(@Nullable Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
TrackSelectionOverride that = (TrackSelectionOverride) obj;
return trackGroup.equals(that.trackGroup) && trackIndexes.equals(that.trackIndexes);
}
@Override
public int hashCode() {
return trackGroup.hashCode() + 31 * trackIndexes.hashCode();
}
private @TrackType int getTrackType() {
return MimeTypes.getTrackType(trackGroup.getFormat(0).sampleMimeType);
}
// Bundleable implementation
@Documented
@Retention(RetentionPolicy.SOURCE)
@IntDef({
FIELD_TRACK_GROUP,
FIELD_TRACKS,
})
private @interface FieldNumber {}
private static final int FIELD_TRACK_GROUP = 0;
private static final int FIELD_TRACKS = 1;
@Override
public Bundle toBundle() {
Bundle bundle = new Bundle();
bundle.putBundle(keyForField(FIELD_TRACK_GROUP), trackGroup.toBundle());
bundle.putIntArray(keyForField(FIELD_TRACKS), Ints.toArray(trackIndexes));
return bundle;
}
/** Object that can restore {@code TrackSelectionOverride} from a {@link Bundle}. */
public static final Creator<TrackSelectionOverride> CREATOR =
bundle -> {
@Nullable Bundle trackGroupBundle = bundle.getBundle(keyForField(FIELD_TRACK_GROUP));
checkNotNull(trackGroupBundle); // Mandatory as there are no reasonable defaults.
TrackGroup trackGroup = TrackGroup.CREATOR.fromBundle(trackGroupBundle);
@Nullable int[] tracks = bundle.getIntArray(keyForField(FIELD_TRACKS));
if (tracks == null) {
return new TrackSelectionOverride(trackGroup);
}
return new TrackSelectionOverride(trackGroup, Ints.asList(tracks));
};
private static String keyForField(@FieldNumber int field) {
return Integer.toString(field, Character.MAX_RADIX);
}
}
/** Empty {@code TrackSelectionOverrides}, where no track selection is overridden. */
public static final TrackSelectionOverrides EMPTY =
new TrackSelectionOverrides(ImmutableMap.of());
private final ImmutableMap<TrackGroup, TrackSelectionOverride> overrides;
private TrackSelectionOverrides(Map<TrackGroup, TrackSelectionOverride> overrides) {
this.overrides = ImmutableMap.copyOf(overrides);
}
/** Returns a {@link Builder} initialized with the values of this instance. */
public Builder buildUpon() {
return new Builder(overrides);
}
/** Returns all {@link TrackSelectionOverride} contained. */
public ImmutableList<TrackSelectionOverride> asList() {
return ImmutableList.copyOf(overrides.values());
}
/**
* Returns the {@link TrackSelectionOverride} of the provided {@link TrackGroup} or {@code null}
* if there is none.
*/
@Nullable
public TrackSelectionOverride getOverride(TrackGroup trackGroup) {
return overrides.get(trackGroup);
}
@Override
public boolean equals(@Nullable Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
TrackSelectionOverrides that = (TrackSelectionOverrides) obj;
return overrides.equals(that.overrides);
}
@Override
public int hashCode() {
return overrides.hashCode();
}
// Bundleable implementation
@Documented
@Retention(RetentionPolicy.SOURCE)
@IntDef({
FIELD_OVERRIDES,
})
private @interface FieldNumber {}
private static final int FIELD_OVERRIDES = 0;
@Override
public Bundle toBundle() {
Bundle bundle = new Bundle();
bundle.putParcelableArrayList(
keyForField(FIELD_OVERRIDES), toBundleArrayList(overrides.values()));
return bundle;
}
/** Object that can restore {@code TrackSelectionOverrides} from a {@link Bundle}. */
public static final Creator<TrackSelectionOverrides> CREATOR =
bundle -> {
List<TrackSelectionOverride> trackSelectionOverrides =
fromBundleNullableList(
TrackSelectionOverride.CREATOR,
bundle.getParcelableArrayList(keyForField(FIELD_OVERRIDES)),
ImmutableList.of());
ImmutableMap.Builder<TrackGroup, TrackSelectionOverride> builder =
new ImmutableMap.Builder<>();
for (int i = 0; i < trackSelectionOverrides.size(); i++) {
TrackSelectionOverride trackSelectionOverride = trackSelectionOverrides.get(i);
builder.put(trackSelectionOverride.trackGroup, trackSelectionOverride);
}
return new TrackSelectionOverrides(builder.build());
};
private static String keyForField(@FieldNumber int field) {
return Integer.toString(field, Character.MAX_RADIX);
}
}

View File

@ -16,8 +16,7 @@
package com.google.android.exoplayer2.trackselection;
import static com.google.android.exoplayer2.util.Assertions.checkNotNull;
import static com.google.android.exoplayer2.util.BundleableUtil.fromBundleNullableList;
import static com.google.android.exoplayer2.util.BundleableUtil.toBundleArrayList;
import static com.google.android.exoplayer2.util.BundleableUtil.fromNullableBundle;
import static com.google.common.base.MoreObjects.firstNonNull;
import android.content.Context;
@ -30,23 +29,17 @@ import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import com.google.android.exoplayer2.Bundleable;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.MediaItem;
import com.google.android.exoplayer2.source.TrackGroup;
import com.google.android.exoplayer2.util.Util;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.primitives.Ints;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import org.checkerframework.checker.initialization.qual.UnknownInitialization;
import org.checkerframework.checker.nullness.qual.EnsuresNonNull;
import org.checkerframework.checker.nullness.qual.NonNull;
/**
* Constraint parameters for track selection.
@ -100,7 +93,7 @@ public class TrackSelectionParameters implements Bundleable {
// General
private boolean forceLowestBitrate;
private boolean forceHighestSupportedBitrate;
private ImmutableMap<TrackGroup, TrackSelectionOverride> trackSelectionOverrides;
private TrackSelectionOverrides trackSelectionOverrides;
private ImmutableSet<@C.TrackType Integer> disabledTrackTypes;
/**
@ -131,7 +124,7 @@ public class TrackSelectionParameters implements Bundleable {
// General
forceLowestBitrate = false;
forceHighestSupportedBitrate = false;
trackSelectionOverrides = ImmutableMap.of();
trackSelectionOverrides = TrackSelectionOverrides.EMPTY;
disabledTrackTypes = ImmutableSet.of();
}
@ -233,17 +226,11 @@ public class TrackSelectionParameters implements Bundleable {
bundle.getBoolean(
keyForField(FIELD_FORCE_HIGHEST_SUPPORTED_BITRATE),
DEFAULT_WITHOUT_CONTEXT.forceHighestSupportedBitrate);
List<TrackGroup> keys =
fromBundleNullableList(
TrackGroup.CREATOR,
bundle.getParcelableArrayList(keyForField(FIELD_SELECTION_OVERRIDE_KEYS)),
ImmutableList.of());
List<TrackSelectionOverride> values =
fromBundleNullableList(
TrackSelectionOverride.CREATOR,
bundle.getParcelableArrayList(keyForField(FIELD_SELECTION_OVERRIDE_VALUES)),
ImmutableList.of());
trackSelectionOverrides = zipToMap(keys, values);
trackSelectionOverrides =
fromNullableBundle(
TrackSelectionOverrides.CREATOR,
bundle.getBundle(keyForField(FIELD_SELECTION_OVERRIDE_KEYS)),
TrackSelectionOverrides.EMPTY);
disabledTrackTypes =
ImmutableSet.copyOf(
Ints.asList(
@ -642,9 +629,8 @@ public class TrackSelectionParameters implements Bundleable {
* @param trackSelectionOverrides The track selection overrides.
* @return This builder.
*/
public Builder setTrackSelectionOverrides(
Map<TrackGroup, TrackSelectionOverride> trackSelectionOverrides) {
this.trackSelectionOverrides = ImmutableMap.copyOf(trackSelectionOverrides);
public Builder setTrackSelectionOverrides(TrackSelectionOverrides trackSelectionOverrides) {
this.trackSelectionOverrides = trackSelectionOverrides;
return this;
}
@ -692,83 +678,6 @@ public class TrackSelectionParameters implements Bundleable {
}
return listBuilder.build();
}
private static <K, V> ImmutableMap<@NonNull K, @NonNull V> zipToMap(
List<@NonNull K> keys, List<@NonNull V> values) {
ImmutableMap.Builder<@NonNull K, @NonNull V> builder = new ImmutableMap.Builder<>();
for (int i = 0; i < keys.size(); i++) {
builder.put(keys.get(i), values.get(i));
}
return builder.build();
}
}
/**
* Forces the selection of {@link #tracks} for a {@link TrackGroup}.
*
* @see #trackSelectionOverrides
*/
public static final class TrackSelectionOverride implements Bundleable {
/** Force the selection of the associated {@link TrackGroup}, but no track will be played. */
public static final TrackSelectionOverride DISABLE =
new TrackSelectionOverride(ImmutableSet.of());
/** The index of tracks in a {@link TrackGroup} to be selected. */
public final ImmutableSet<Integer> tracks;
/** Constructs an instance to force {@code tracks} to be selected. */
public TrackSelectionOverride(ImmutableSet<Integer> tracks) {
this.tracks = tracks;
}
@Override
public boolean equals(@Nullable Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
TrackSelectionOverride that = (TrackSelectionOverride) obj;
return tracks.equals(that.tracks);
}
@Override
public int hashCode() {
return tracks.hashCode();
}
// Bundleable implementation
@Documented
@Retention(RetentionPolicy.SOURCE)
@IntDef({
FIELD_TRACKS,
})
private @interface FieldNumber {}
private static final int FIELD_TRACKS = 0;
@Override
public Bundle toBundle() {
Bundle bundle = new Bundle();
bundle.putIntArray(keyForField(FIELD_TRACKS), Ints.toArray(tracks));
return bundle;
}
/** Object that can restore {@code TrackSelectionOverride} from a {@link Bundle}. */
public static final Creator<TrackSelectionOverride> CREATOR =
bundle -> {
@Nullable int[] tracks = bundle.getIntArray(keyForField(FIELD_TRACKS));
if (tracks == null) {
return DISABLE;
}
return new TrackSelectionOverride(ImmutableSet.copyOf(Ints.asList(tracks)));
};
private static String keyForField(@FieldNumber int field) {
return Integer.toString(field, Character.MAX_RADIX);
}
}
/**
@ -921,29 +830,8 @@ public class TrackSelectionParameters implements Bundleable {
*/
public final boolean forceHighestSupportedBitrate;
/**
* For each {@link TrackGroup} in the map, forces the tracks associated with it to be selected for
* playback.
*
* <p>For example if {@code trackSelectionOverrides.equals(ImmutableMap.of(trackGroup,
* ImmutableSet.of(1, 2, 3)))}, the tracks 1, 2 and 3 of {@code trackGroup} will be selected.
*
* <p>If multiple of the current {@link TrackGroup}s of the same {@link C.TrackType} are
* overridden, it is undetermined which one(s) will be selected. For example if a {@link
* MediaItem} has 2 video track groups (for example 2 different angles), and both are overriden,
* it is undetermined which one will be selected.
*
* <p>If multiple tracks of the {@link TrackGroup} are overriden, all supported (see {@link
* C.FormatSupport}) will be selected.
*
* <p>If a {@link TrackGroup} is associated with an empty set of tracks, no tracks will be played.
* This is similar to {@link #disabledTrackTypes}, except it will only affect the playback of the
* associated {@link TrackGroup}. For example, if the {@link C#TRACK_TYPE_VIDEO} {@link
* TrackGroup} is associated with no tracks, no video will play until the next video starts.
*
* <p>The default value is that no {@link TrackGroup} selections are overridden (empty map).
*/
public final ImmutableMap<TrackGroup, TrackSelectionOverride> trackSelectionOverrides;
/** Overrides to force tracks to be selected. */
public final TrackSelectionOverrides trackSelectionOverrides;
/**
* The track types that are disabled. No track of a disabled type will be selected, thus no track
* type contained in the set will be played. The default value is that no track type is disabled
@ -1159,12 +1047,8 @@ public class TrackSelectionParameters implements Bundleable {
bundle.putBoolean(keyForField(FIELD_FORCE_LOWEST_BITRATE), forceLowestBitrate);
bundle.putBoolean(
keyForField(FIELD_FORCE_HIGHEST_SUPPORTED_BITRATE), forceHighestSupportedBitrate);
bundle.putParcelableArrayList(
keyForField(FIELD_SELECTION_OVERRIDE_KEYS),
toBundleArrayList(trackSelectionOverrides.keySet()));
bundle.putParcelableArrayList(
keyForField(FIELD_SELECTION_OVERRIDE_VALUES),
toBundleArrayList(trackSelectionOverrides.values()));
bundle.putBundle(
keyForField(FIELD_SELECTION_OVERRIDE_KEYS), trackSelectionOverrides.toBundle());
bundle.putIntArray(keyForField(FIELD_DISABLED_TRACK_TYPE), Ints.toArray(disabledTrackTypes));
return bundle;

View File

@ -0,0 +1,176 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.exoplayer2.trackselection;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertThrows;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.android.exoplayer2.trackselection.TrackSelectionOverrides.TrackSelectionOverride;
import com.google.common.collect.ImmutableList;
import java.util.Arrays;
import org.junit.Test;
import org.junit.runner.RunWith;
// packages.bara.sky: import com.google.android.exoplayer2.util.MimeTypes;
// packages.bara.sky: import com.google.android.exoplayer2.Bundleable;
// packages.bara.sky: import com.google.android.exoplayer2.C;
// packages.bara.sky: import com.google.android.exoplayer2.Format;
/** Unit tests for {@link TrackSelectionOverrides}. */
@RunWith(AndroidJUnit4.class)
public final class TrackSelectionOverridesTest {
public static final TrackGroup AAC_TRACK_GROUP =
new TrackGroup(new Format.Builder().setSampleMimeType(MimeTypes.AUDIO_AAC).build());
private static TrackGroup newTrackGroupWithIds(int... ids) {
return new TrackGroup(
Arrays.stream(ids)
.mapToObj(id -> new Format.Builder().setId(id).build())
.toArray(Format[]::new));
}
@Test
public void newTrackSelectionOverride_withJustTrackGroup_selectsAllTracks() {
TrackSelectionOverride trackSelectionOverride =
new TrackSelectionOverride(newTrackGroupWithIds(1, 2));
assertThat(trackSelectionOverride.trackGroup).isEqualTo(newTrackGroupWithIds(1, 2));
assertThat(trackSelectionOverride.trackIndexes).containsExactly(0, 1).inOrder();
}
@Test
public void newTrackSelectionOverride_withTracks_selectsOnlySpecifiedTracks() {
TrackSelectionOverride trackSelectionOverride =
new TrackSelectionOverride(newTrackGroupWithIds(1, 2), ImmutableList.of(1));
assertThat(trackSelectionOverride.trackGroup).isEqualTo(newTrackGroupWithIds(1, 2));
assertThat(trackSelectionOverride.trackIndexes).containsExactly(1);
}
@Test
public void newTrackSelectionOverride_with0Tracks_selectsAllSpecifiedTracks() {
TrackSelectionOverride trackSelectionOverride =
new TrackSelectionOverride(newTrackGroupWithIds(1, 2), ImmutableList.of());
assertThat(trackSelectionOverride.trackGroup).isEqualTo(newTrackGroupWithIds(1, 2));
assertThat(trackSelectionOverride.trackIndexes).isEmpty();
}
@Test
public void newTrackSelectionOverride_withInvalidIndex_throws() {
assertThrows(
IndexOutOfBoundsException.class,
() -> new TrackSelectionOverride(newTrackGroupWithIds(1, 2), ImmutableList.of(2)));
}
@Test
public void roundTripViaBundle_withOverrides_yieldsEqualInstance() {
TrackSelectionOverrides trackSelectionOverrides =
new TrackSelectionOverrides.Builder()
.setOverrideForType(
new TrackSelectionOverride(newTrackGroupWithIds(3, 4), ImmutableList.of(1)))
.addOverride(new TrackSelectionOverride(newTrackGroupWithIds(5, 6)))
.build();
TrackSelectionOverrides fromBundle =
TrackSelectionOverrides.CREATOR.fromBundle(trackSelectionOverrides.toBundle());
assertThat(fromBundle).isEqualTo(trackSelectionOverrides);
assertThat(fromBundle.asList()).isEqualTo(trackSelectionOverrides.asList());
}
@Test
public void builder_byDefault_isEmpty() {
TrackSelectionOverrides trackSelectionOverrides = new TrackSelectionOverrides.Builder().build();
assertThat(trackSelectionOverrides.asList()).isEmpty();
assertThat(trackSelectionOverrides).isEqualTo(TrackSelectionOverrides.EMPTY);
}
@Test
public void addOverride_onDifferentGroups_addsOverride() {
TrackSelectionOverride override1 = new TrackSelectionOverride(newTrackGroupWithIds(1));
TrackSelectionOverride override2 = new TrackSelectionOverride(newTrackGroupWithIds(2));
TrackSelectionOverrides trackSelectionOverrides =
new TrackSelectionOverrides.Builder().addOverride(override1).addOverride(override2).build();
assertThat(trackSelectionOverrides.asList()).containsExactly(override1, override2);
assertThat(trackSelectionOverrides.getOverride(override1.trackGroup)).isEqualTo(override1);
assertThat(trackSelectionOverrides.getOverride(override2.trackGroup)).isEqualTo(override2);
}
@Test
public void addOverride_onSameGroup_replacesOverride() {
TrackGroup trackGroup = newTrackGroupWithIds(1, 2, 3);
TrackSelectionOverride override1 =
new TrackSelectionOverride(trackGroup, /* trackIndexes= */ ImmutableList.of(0));
TrackSelectionOverride override2 =
new TrackSelectionOverride(trackGroup, /* trackIndexes= */ ImmutableList.of(1));
TrackSelectionOverrides trackSelectionOverrides =
new TrackSelectionOverrides.Builder().addOverride(override1).addOverride(override2).build();
assertThat(trackSelectionOverrides.asList()).containsExactly(override2);
assertThat(trackSelectionOverrides.getOverride(override2.trackGroup)).isEqualTo(override2);
}
@Test
public void setOverrideForType_onSameType_replacesOverride() {
TrackSelectionOverride override1 = new TrackSelectionOverride(newTrackGroupWithIds(1));
TrackSelectionOverride override2 = new TrackSelectionOverride(newTrackGroupWithIds(2));
TrackSelectionOverrides trackSelectionOverrides =
new TrackSelectionOverrides.Builder()
.setOverrideForType(override1)
.setOverrideForType(override2)
.build();
assertThat(trackSelectionOverrides.asList()).containsExactly(override2);
assertThat(trackSelectionOverrides.getOverride(override2.trackGroup)).isEqualTo(override2);
}
@Test
public void clearOverridesOfType_ofTypeAudio_removesAudioOverride() {
TrackSelectionOverride override1 = new TrackSelectionOverride(AAC_TRACK_GROUP);
TrackSelectionOverride override2 = new TrackSelectionOverride(newTrackGroupWithIds(1));
TrackSelectionOverrides trackSelectionOverrides =
new TrackSelectionOverrides.Builder()
.addOverride(override1)
.addOverride(override2)
.clearOverridesOfType(C.TRACK_TYPE_AUDIO)
.build();
assertThat(trackSelectionOverrides.asList()).containsExactly(override2);
assertThat(trackSelectionOverrides.getOverride(override2.trackGroup)).isEqualTo(override2);
}
@Test
public void clearOverride_ofTypeGroup_removesOverride() {
TrackSelectionOverride override1 = new TrackSelectionOverride(AAC_TRACK_GROUP);
TrackSelectionOverride override2 = new TrackSelectionOverride(newTrackGroupWithIds(1));
TrackSelectionOverrides trackSelectionOverrides =
new TrackSelectionOverrides.Builder()
.addOverride(override1)
.addOverride(override2)
.clearOverride(override2.trackGroup)
.build();
assertThat(trackSelectionOverrides.asList()).containsExactly(override1);
assertThat(trackSelectionOverrides.getOverride(override1.trackGroup)).isEqualTo(override1);
}
}

View File

@ -23,16 +23,16 @@ import com.google.android.exoplayer2.Bundleable;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector.SelectionOverride;
import com.google.android.exoplayer2.trackselection.TrackSelectionParameters.TrackSelectionOverride;
import com.google.android.exoplayer2.trackselection.TrackSelectionOverrides.TrackSelectionOverride;
import com.google.android.exoplayer2.util.MimeTypes;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import org.junit.Test;
import org.junit.runner.RunWith;
/** Tests for {@link TrackSelectionParameters}. */
@RunWith(AndroidJUnit4.class)
public class TrackSelectionParametersTest {
public final class TrackSelectionParametersTest {
@Test
public void defaultValue_withoutChange_isAsExpected() {
@ -64,16 +64,22 @@ public class TrackSelectionParametersTest {
// General
assertThat(parameters.forceLowestBitrate).isFalse();
assertThat(parameters.forceHighestSupportedBitrate).isFalse();
assertThat(parameters.trackSelectionOverrides).isEmpty();
assertThat(parameters.trackSelectionOverrides.asList()).isEmpty();
assertThat(parameters.disabledTrackTypes).isEmpty();
}
@Test
public void parametersSet_fromDefault_isAsExpected() {
ImmutableMap<TrackGroup, TrackSelectionOverride> trackSelectionOverrides =
ImmutableMap.of(
new TrackGroup(new Format.Builder().build()),
new TrackSelectionOverride(/* tracks= */ ImmutableSet.of(2, 3)));
TrackSelectionOverrides trackSelectionOverrides =
new TrackSelectionOverrides.Builder()
.addOverride(new TrackSelectionOverride(new TrackGroup(new Format.Builder().build())))
.addOverride(
new TrackSelectionOverride(
new TrackGroup(
new Format.Builder().setId(4).build(),
new Format.Builder().setId(5).build()),
/* trackIndexes= */ ImmutableList.of(1)))
.build();
TrackSelectionParameters parameters =
TrackSelectionParameters.DEFAULT_WITHOUT_CONTEXT
.buildUpon()

View File

@ -39,7 +39,7 @@ import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId;
import com.google.android.exoplayer2.source.TrackGroup;
import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.trackselection.TrackSelectionParameters.TrackSelectionOverride;
import com.google.android.exoplayer2.trackselection.TrackSelectionOverrides.TrackSelectionOverride;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.BundleableUtil;
import com.google.android.exoplayer2.util.Util;
@ -611,7 +611,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
@Override
public ParametersBuilder setTrackSelectionOverrides(
Map<TrackGroup, TrackSelectionOverride> trackSelectionOverrides) {
TrackSelectionOverrides trackSelectionOverrides) {
super.setTrackSelectionOverrides(trackSelectionOverrides);
return this;
}
@ -710,7 +710,9 @@ public class DefaultTrackSelector extends MappingTrackSelector {
* @param groups The {@link TrackGroupArray} for which the override should be applied.
* @param override The override.
* @return This builder.
* @deprecated Use {@link TrackSelectionParameters.Builder#setTrackSelectionOverrides}.
*/
@Deprecated
public final ParametersBuilder setSelectionOverride(
int rendererIndex, TrackGroupArray groups, @Nullable SelectionOverride override) {
Map<TrackGroupArray, @NullableType SelectionOverride> overrides =
@ -733,7 +735,9 @@ public class DefaultTrackSelector extends MappingTrackSelector {
* @param rendererIndex The renderer index.
* @param groups The {@link TrackGroupArray} for which the override should be cleared.
* @return This builder.
* @deprecated Use {@link TrackSelectionParameters.Builder#setTrackSelectionOverrides}.
*/
@Deprecated
public final ParametersBuilder clearSelectionOverride(
int rendererIndex, TrackGroupArray groups) {
Map<TrackGroupArray, @NullableType SelectionOverride> overrides =
@ -754,7 +758,9 @@ public class DefaultTrackSelector extends MappingTrackSelector {
*
* @param rendererIndex The renderer index.
* @return This builder.
* @deprecated Use {@link TrackSelectionParameters.Builder#setTrackSelectionOverrides}.
*/
@Deprecated
public final ParametersBuilder clearSelectionOverrides(int rendererIndex) {
Map<TrackGroupArray, @NullableType SelectionOverride> overrides =
selectionOverrides.get(rendererIndex);
@ -770,7 +776,9 @@ public class DefaultTrackSelector extends MappingTrackSelector {
* Clears all track selection overrides for all renderers.
*
* @return This builder.
* @deprecated Use {@link TrackSelectionParameters.Builder#setTrackSelectionOverrides}.
*/
@Deprecated
public final ParametersBuilder clearSelectionOverrides() {
if (selectionOverrides.size() == 0) {
// Nothing to clear.
@ -1560,9 +1568,11 @@ public class DefaultTrackSelector extends MappingTrackSelector {
for (int j = 0; j < rendererTrackGroups.length; j++) {
TrackGroup trackGroup = rendererTrackGroups.get(j);
@Nullable
TrackSelectionOverride overrideTracks = params.trackSelectionOverrides.get(trackGroup);
TrackSelectionOverride overrideTracks =
params.trackSelectionOverrides.getOverride(trackGroup);
if (overrideTracks != null) {
return new ExoTrackSelection.Definition(trackGroup, Ints.toArray(overrideTracks.tracks));
return new ExoTrackSelection.Definition(
trackGroup, Ints.toArray(overrideTracks.trackIndexes));
}
}
return currentDefinition; // No override

View File

@ -17,21 +17,11 @@ package com.google.android.exoplayer2.trackselection;
import android.os.SystemClock;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.TracksInfo;
import com.google.android.exoplayer2.TracksInfo.TrackGroupInfo;
import com.google.android.exoplayer2.source.TrackGroup;
import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector.SelectionOverride;
import com.google.android.exoplayer2.trackselection.ExoTrackSelection.Definition;
import com.google.android.exoplayer2.trackselection.TrackSelectionParameters.TrackSelectionOverride;
import com.google.android.exoplayer2.upstream.LoadErrorHandlingPolicy;
import com.google.android.exoplayer2.util.MimeTypes;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import java.util.Map;
import org.checkerframework.checker.nullness.compatqual.NullableType;
import org.checkerframework.dataflow.qual.Pure;
/** Track selection related utility methods. */
public final class TrackSelectionUtil {
@ -134,67 +124,4 @@ public final class TrackSelectionUtil {
numberOfTracks,
numberOfExcludedTracks);
}
/**
* Forces tracks in a {@link TrackGroup} to be the only ones selected for a {@link C.TrackType}.
* No other tracks of that type will be selectable. If the forced tracks are not supported, then
* no tracks of that type will be selected.
*
* @param trackSelectionOverrides The current {@link TrackSelectionOverride overrides}.
* @param tracksInfo The current {@link TracksInfo}.
* @param forcedTrackGroupIndex The index of the {@link TrackGroup} in {@code tracksInfo} that
* should have its track selected.
* @param forcedTrackSelectionOverride The tracks to force selection of.
* @return The updated {@link TrackSelectionOverride overrides}.
*/
@Pure
public static ImmutableMap<TrackGroup, TrackSelectionOverride> forceTrackSelection(
ImmutableMap<TrackGroup, TrackSelectionOverride> trackSelectionOverrides,
TracksInfo tracksInfo,
int forcedTrackGroupIndex,
TrackSelectionOverride forcedTrackSelectionOverride) {
@C.TrackType
int trackType = tracksInfo.getTrackGroupInfos().get(forcedTrackGroupIndex).getTrackType();
ImmutableMap.Builder<TrackGroup, TrackSelectionOverride> overridesBuilder =
new ImmutableMap.Builder<>();
// Maintain overrides for the other track types.
for (Map.Entry<TrackGroup, TrackSelectionOverride> entry : trackSelectionOverrides.entrySet()) {
if (MimeTypes.getTrackType(entry.getKey().getFormat(0).sampleMimeType) != trackType) {
overridesBuilder.put(entry);
}
}
ImmutableList<TrackGroupInfo> trackGroupInfos = tracksInfo.getTrackGroupInfos();
for (int i = 0; i < trackGroupInfos.size(); i++) {
TrackGroup trackGroup = trackGroupInfos.get(i).getTrackGroup();
if (i == forcedTrackGroupIndex) {
overridesBuilder.put(trackGroup, forcedTrackSelectionOverride);
} else {
overridesBuilder.put(trackGroup, TrackSelectionOverride.DISABLE);
}
}
return overridesBuilder.build();
}
/**
* Removes all {@link TrackSelectionOverride overrides} associated with {@link TrackGroup
* TrackGroups} of type {@code trackType}.
*
* @param trackType The {@link C.TrackType} of all overrides to remove.
* @param trackSelectionOverrides The current {@link TrackSelectionOverride overrides}.
* @return The updated {@link TrackSelectionOverride overrides}.
*/
@Pure
public static ImmutableMap<TrackGroup, TrackSelectionOverride>
clearTrackSelectionOverridesForType(
@C.TrackType int trackType,
ImmutableMap<TrackGroup, TrackSelectionOverride> trackSelectionOverrides) {
ImmutableMap.Builder<TrackGroup, TrackSelectionOverride> overridesBuilder =
ImmutableMap.builder();
for (Map.Entry<TrackGroup, TrackSelectionOverride> entry : trackSelectionOverrides.entrySet()) {
if (MimeTypes.getTrackType(entry.getKey().getFormat(0).sampleMimeType) != trackType) {
overridesBuilder.put(entry);
}
}
return overridesBuilder.build();
}
}

View File

@ -46,13 +46,12 @@ import com.google.android.exoplayer2.testutil.FakeTimeline;
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector.Parameters;
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector.ParametersBuilder;
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector.SelectionOverride;
import com.google.android.exoplayer2.trackselection.TrackSelectionParameters.TrackSelectionOverride;
import com.google.android.exoplayer2.trackselection.TrackSelectionOverrides.TrackSelectionOverride;
import com.google.android.exoplayer2.trackselection.TrackSelector.InvalidationListener;
import com.google.android.exoplayer2.upstream.BandwidthMeter;
import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.Util;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import java.util.HashMap;
import java.util.Map;
@ -158,12 +157,15 @@ public final class DefaultTrackSelectorTest {
/** Tests that an empty override clears a track selection. */
@Test
public void selectTracks_withNullOverride_clearsTrackSelection() throws ExoPlaybackException {
public void selectTracks_withOverrideWithoutTracks_clearsTrackSelection()
throws ExoPlaybackException {
trackSelector.setParameters(
trackSelector
.buildUponParameters()
.setTrackSelectionOverrides(
ImmutableMap.of(VIDEO_TRACK_GROUP, new TrackSelectionOverride(ImmutableSet.of()))));
new TrackSelectionOverrides.Builder()
.addOverride(new TrackSelectionOverride(VIDEO_TRACK_GROUP, ImmutableList.of()))
.build()));
TrackSelectorResult result =
trackSelector.selectTracks(RENDERER_CAPABILITIES, TRACK_GROUPS, periodId, TIMELINE);
@ -210,8 +212,11 @@ public final class DefaultTrackSelectorTest {
trackSelector
.buildUponParameters()
.setTrackSelectionOverrides(
ImmutableMap.of(
new TrackGroup(VIDEO_FORMAT, VIDEO_FORMAT), TrackSelectionOverride.DISABLE)));
new TrackSelectionOverrides.Builder()
.setOverrideForType(
new TrackSelectionOverride(
new TrackGroup(VIDEO_FORMAT, VIDEO_FORMAT), ImmutableList.of()))
.build()));
TrackSelectorResult result =
trackSelector.selectTracks(
@ -1874,9 +1879,12 @@ public final class DefaultTrackSelectorTest {
.setRendererDisabled(3, true)
.setRendererDisabled(5, false)
.setTrackSelectionOverrides(
ImmutableMap.of(
AUDIO_TRACK_GROUP,
new TrackSelectionOverride(/* tracks= */ ImmutableSet.of(3, 4, 5))))
new TrackSelectionOverrides.Builder()
.setOverrideForType(
new TrackSelectionOverride(
new TrackGroup(AUDIO_FORMAT, AUDIO_FORMAT, AUDIO_FORMAT, AUDIO_FORMAT),
/* trackIndexes= */ ImmutableList.of(0, 2, 3)))
.build())
.setDisabledTrackTypes(ImmutableSet.of(C.TRACK_TYPE_AUDIO))
.build();
}

View File

@ -32,9 +32,8 @@ import static com.google.android.exoplayer2.Player.EVENT_SEEK_FORWARD_INCREMENT_
import static com.google.android.exoplayer2.Player.EVENT_SHUFFLE_MODE_ENABLED_CHANGED;
import static com.google.android.exoplayer2.Player.EVENT_TIMELINE_CHANGED;
import static com.google.android.exoplayer2.Player.EVENT_TRACKS_CHANGED;
import static com.google.android.exoplayer2.trackselection.TrackSelectionUtil.clearTrackSelectionOverridesForType;
import static com.google.android.exoplayer2.trackselection.TrackSelectionUtil.forceTrackSelection;
import static com.google.android.exoplayer2.util.Assertions.checkNotNull;
import static com.google.android.exoplayer2.util.Util.castNonNull;
import android.annotation.SuppressLint;
import android.content.Context;
@ -68,13 +67,13 @@ import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.TracksInfo;
import com.google.android.exoplayer2.TracksInfo.TrackGroupInfo;
import com.google.android.exoplayer2.source.TrackGroup;
import com.google.android.exoplayer2.trackselection.TrackSelectionOverrides;
import com.google.android.exoplayer2.trackselection.TrackSelectionOverrides.TrackSelectionOverride;
import com.google.android.exoplayer2.trackselection.TrackSelectionParameters;
import com.google.android.exoplayer2.trackselection.TrackSelectionParameters.TrackSelectionOverride;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.RepeatModeUtil;
import com.google.android.exoplayer2.util.Util;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import java.util.ArrayList;
import java.util.Arrays;
@ -82,8 +81,8 @@ import java.util.Collections;
import java.util.Formatter;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;
import org.checkerframework.dataflow.qual.Pure;
/**
* A view for controlling {@link Player} instances.
@ -2032,14 +2031,8 @@ public class StyledPlayerControlView extends FrameLayout {
// Audio track selection option includes "Auto" at the top.
holder.textView.setText(R.string.exo_track_selection_auto);
// hasSelectionOverride is true means there is an explicit track selection, not "Auto".
boolean hasSelectionOverride = false;
TrackSelectionParameters parameters = checkNotNull(player).getTrackSelectionParameters();
for (int i = 0; i < tracks.size(); i++) {
if (parameters.trackSelectionOverrides.containsKey(tracks.get(i).trackGroup)) {
hasSelectionOverride = true;
break;
}
}
boolean hasSelectionOverride = hasSelectionOverride(parameters.trackSelectionOverrides);
holder.checkView.setVisibility(hasSelectionOverride ? INVISIBLE : VISIBLE);
holder.itemView.setOnClickListener(
v -> {
@ -2048,15 +2041,18 @@ public class StyledPlayerControlView extends FrameLayout {
}
TrackSelectionParameters trackSelectionParameters =
player.getTrackSelectionParameters();
// Remove all audio overrides.
ImmutableMap<TrackGroup, TrackSelectionOverride> trackSelectionOverrides =
clearTrackSelectionOverridesForType(
C.TRACK_TYPE_AUDIO, trackSelectionParameters.trackSelectionOverrides);
player.setTrackSelectionParameters(
TrackSelectionOverrides trackSelectionOverrides =
trackSelectionParameters
.trackSelectionOverrides
.buildUpon()
.setTrackSelectionOverrides(trackSelectionOverrides)
.build());
.clearOverridesOfType(C.TRACK_TYPE_AUDIO)
.build();
castNonNull(player)
.setTrackSelectionParameters(
trackSelectionParameters
.buildUpon()
.setTrackSelectionOverrides(trackSelectionOverrides)
.build());
settingsAdapter.setSubTextAtPosition(
SETTINGS_AUDIO_TRACK_SELECTION_POSITION,
getResources().getString(R.string.exo_track_selection_auto));
@ -2064,6 +2060,21 @@ public class StyledPlayerControlView extends FrameLayout {
});
}
private boolean hasSelectionOverride(TrackSelectionOverrides trackSelectionOverrides) {
int previousTrackGroupIndex = C.INDEX_UNSET;
for (int i = 0; i < tracks.size(); i++) {
TrackInformation track = tracks.get(i);
if (track.trackGroupIndex == previousTrackGroupIndex) {
continue;
}
if (trackSelectionOverrides.getOverride(track.trackGroup) != null) {
return true;
}
previousTrackGroupIndex = track.trackGroupIndex;
}
return false;
}
@Override
public void onTrackSelection(String subtext) {
settingsAdapter.setSubTextAtPosition(SETTINGS_AUDIO_TRACK_SELECTION_POSITION, subtext);
@ -2075,9 +2086,10 @@ public class StyledPlayerControlView extends FrameLayout {
boolean hasSelectionOverride = false;
for (int i = 0; i < trackInformations.size(); i++) {
if (checkNotNull(player)
.getTrackSelectionParameters()
.trackSelectionOverrides
.containsKey(trackInformations.get(i).trackGroup)) {
.getTrackSelectionParameters()
.trackSelectionOverrides
.getOverride(trackInformations.get(i).trackGroup)
!= null) {
hasSelectionOverride = true;
break;
}
@ -2140,9 +2152,10 @@ public class StyledPlayerControlView extends FrameLayout {
TrackInformation track = tracks.get(position - 1);
boolean explicitlySelected =
checkNotNull(player)
.getTrackSelectionParameters()
.trackSelectionOverrides
.containsKey(track.trackGroup)
.getTrackSelectionParameters()
.trackSelectionOverrides
.getOverride(track.trackGroup)
!= null
&& track.isSelected();
holder.textView.setText(track.trackName);
holder.checkView.setVisibility(explicitlySelected ? VISIBLE : INVISIBLE);
@ -2153,12 +2166,13 @@ public class StyledPlayerControlView extends FrameLayout {
}
TrackSelectionParameters trackSelectionParameters =
player.getTrackSelectionParameters();
Map<TrackGroup, TrackSelectionOverride> overrides =
TrackSelectionOverrides overrides =
forceTrackSelection(
trackSelectionParameters.trackSelectionOverrides,
track.tracksInfo,
track.trackGroupIndex,
new TrackSelectionOverride(ImmutableSet.of(track.trackIndex)));
new TrackSelectionOverride(
track.trackGroup, ImmutableList.of(track.trackIndex)));
checkNotNull(player)
.setTrackSelectionParameters(
trackSelectionParameters
@ -2196,4 +2210,41 @@ public class StyledPlayerControlView extends FrameLayout {
checkView = itemView.findViewById(R.id.exo_check);
}
}
/**
* Forces tracks in a {@link TrackGroup} to be the only ones selected for a {@link C.TrackType}.
* No other tracks of that type will be selectable. If the forced tracks are not supported, then
* no tracks of that type will be selected.
*
* @param trackSelectionOverrides The current {@link TrackSelectionOverride overrides}.
* @param tracksInfo The current {@link TracksInfo}.
* @param forcedTrackGroupIndex The index of the {@link TrackGroup} in {@code tracksInfo} that
* should have its track selected.
* @param forcedTrackSelectionOverride The tracks to force selection of.
* @return The updated {@link TrackSelectionOverride overrides}.
*/
@Pure
private static TrackSelectionOverrides forceTrackSelection(
TrackSelectionOverrides trackSelectionOverrides,
TracksInfo tracksInfo,
int forcedTrackGroupIndex,
TrackSelectionOverride forcedTrackSelectionOverride) {
TrackSelectionOverrides.Builder overridesBuilder = trackSelectionOverrides.buildUpon();
@C.TrackType
int trackType = tracksInfo.getTrackGroupInfos().get(forcedTrackGroupIndex).getTrackType();
overridesBuilder.setOverrideForType(forcedTrackSelectionOverride);
// TrackSelectionOverride doesn't currently guarantee that only overwritten track
// group of a given type are selected, so the others have to be explicitly disabled.
// This guarantee is provided in the following patch that removes the need for this method.
ImmutableList<TrackGroupInfo> trackGroupInfos = tracksInfo.getTrackGroupInfos();
for (int i = 0; i < trackGroupInfos.size(); i++) {
TrackGroupInfo trackGroupInfo = trackGroupInfos.get(i);
if (i != forcedTrackGroupIndex && trackGroupInfo.getTrackType() == trackType) {
TrackGroup trackGroup = trackGroupInfo.getTrackGroup();
overridesBuilder.addOverride(new TrackSelectionOverride(trackGroup, ImmutableList.of()));
}
}
return overridesBuilder.build();
}
}