diff --git a/extensions/cast/src/main/java/com/google/android/exoplayer2/ext/cast/CastPlayer.java b/extensions/cast/src/main/java/com/google/android/exoplayer2/ext/cast/CastPlayer.java
index 22dc83818e..e3f075e562 100644
--- a/extensions/cast/src/main/java/com/google/android/exoplayer2/ext/cast/CastPlayer.java
+++ b/extensions/cast/src/main/java/com/google/android/exoplayer2/ext/cast/CastPlayer.java
@@ -37,6 +37,7 @@ import com.google.android.exoplayer2.PlaybackException;
import com.google.android.exoplayer2.PlaybackParameters;
import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.Timeline;
+import com.google.android.exoplayer2.TracksInfo;
import com.google.android.exoplayer2.audio.AudioAttributes;
import com.google.android.exoplayer2.source.TrackGroup;
import com.google.android.exoplayer2.source.TrackGroupArray;
@@ -66,6 +67,7 @@ import com.google.android.gms.common.api.PendingResult;
import com.google.android.gms.common.api.ResultCallback;
import com.google.common.collect.ImmutableList;
import java.util.List;
+import org.checkerframework.checker.nullness.compatqual.NullableType;
import org.checkerframework.checker.nullness.qual.RequiresNonNull;
/**
@@ -101,7 +103,8 @@ public final class CastPlayer extends BasePlayer {
COMMAND_GET_TIMELINE,
COMMAND_GET_MEDIA_ITEMS_METADATA,
COMMAND_SET_MEDIA_ITEMS_METADATA,
- COMMAND_CHANGE_MEDIA_ITEMS)
+ COMMAND_CHANGE_MEDIA_ITEMS,
+ COMMAND_GET_TRACK_INFOS)
.build();
public static final float MIN_SPEED_SUPPORTED = 0.5f;
@@ -142,6 +145,7 @@ public final class CastPlayer extends BasePlayer {
private CastTimeline currentTimeline;
private TrackGroupArray currentTrackGroups;
private TrackSelectionArray currentTrackSelection;
+ private TracksInfo currentTracksInfo;
private Commands availableCommands;
@Player.State private int playbackState;
private int currentWindowIndex;
@@ -219,6 +223,7 @@ public final class CastPlayer extends BasePlayer {
currentTimeline = CastTimeline.EMPTY_CAST_TIMELINE;
currentTrackGroups = TrackGroupArray.EMPTY;
currentTrackSelection = EMPTY_TRACK_SELECTION_ARRAY;
+ currentTracksInfo = TracksInfo.EMPTY;
availableCommands = new Commands.Builder().addAll(PERMANENT_AVAILABLE_COMMANDS).build();
pendingSeekWindowIndex = C.INDEX_UNSET;
pendingSeekPositionMs = C.TIME_UNSET;
@@ -583,14 +588,19 @@ public final class CastPlayer extends BasePlayer {
return false;
}
+ @Override
+ public TrackGroupArray getCurrentTrackGroups() {
+ return currentTrackGroups;
+ }
+
@Override
public TrackSelectionArray getCurrentTrackSelections() {
return currentTrackSelection;
}
@Override
- public TrackGroupArray getCurrentTrackGroups() {
- return currentTrackGroups;
+ public TracksInfo getCurrentTracksInfo() {
+ return currentTracksInfo;
}
@Override
@@ -871,6 +881,8 @@ public final class CastPlayer extends BasePlayer {
listeners.queueEvent(
Player.EVENT_TRACKS_CHANGED,
listener -> listener.onTracksChanged(currentTrackGroups, currentTrackSelection));
+ listeners.queueEvent(
+ Player.EVENT_TRACKS_CHANGED, listener -> listener.onTracksInfoChanged(currentTracksInfo));
}
updateAvailableCommandsAndNotifyIfChanged();
listeners.flushEvents();
@@ -1032,6 +1044,7 @@ public final class CastPlayer extends BasePlayer {
boolean hasChanged = !currentTrackGroups.isEmpty();
currentTrackGroups = TrackGroupArray.EMPTY;
currentTrackSelection = EMPTY_TRACK_SELECTION_ARRAY;
+ currentTracksInfo = TracksInfo.EMPTY;
return hasChanged;
}
long[] activeTrackIds = mediaStatus.getActiveTrackIds();
@@ -1040,7 +1053,9 @@ public final class CastPlayer extends BasePlayer {
}
TrackGroup[] trackGroups = new TrackGroup[castMediaTracks.size()];
- TrackSelection[] trackSelections = new TrackSelection[RENDERER_COUNT];
+ @NullableType TrackSelection[] trackSelections = new TrackSelection[RENDERER_COUNT];
+ TracksInfo.TrackGroupInfo[] trackGroupInfos =
+ new TracksInfo.TrackGroupInfo[castMediaTracks.size()];
for (int i = 0; i < castMediaTracks.size(); i++) {
MediaTrack mediaTrack = castMediaTracks.get(i);
trackGroups[i] = new TrackGroup(CastUtils.mediaTrackToFormat(mediaTrack));
@@ -1048,19 +1063,28 @@ public final class CastPlayer extends BasePlayer {
long id = mediaTrack.getId();
@C.TrackType int trackType = MimeTypes.getTrackType(mediaTrack.getContentType());
int rendererIndex = getRendererIndexForTrackType(trackType);
- if (isTrackActive(id, activeTrackIds)
- && rendererIndex != C.INDEX_UNSET
- && trackSelections[rendererIndex] == null) {
+ boolean supported = rendererIndex != C.INDEX_UNSET;
+ boolean selected =
+ isTrackActive(id, activeTrackIds) && supported && trackSelections[rendererIndex] == null;
+ if (selected) {
trackSelections[rendererIndex] = new CastTrackSelection(trackGroups[i]);
}
+ @C.FormatSupport
+ int[] trackSupport = new int[] {supported ? C.FORMAT_HANDLED : C.FORMAT_UNSUPPORTED_TYPE};
+ final boolean[] trackSelected = new boolean[] {selected};
+ trackGroupInfos[i] =
+ new TracksInfo.TrackGroupInfo(trackGroups[i], trackSupport, trackType, trackSelected);
}
TrackGroupArray newTrackGroups = new TrackGroupArray(trackGroups);
TrackSelectionArray newTrackSelections = new TrackSelectionArray(trackSelections);
+ TracksInfo newTracksInfo = new TracksInfo(ImmutableList.copyOf(trackGroupInfos));
if (!newTrackGroups.equals(currentTrackGroups)
- || !newTrackSelections.equals(currentTrackSelection)) {
- currentTrackSelection = new TrackSelectionArray(trackSelections);
- currentTrackGroups = new TrackGroupArray(trackGroups);
+ || !newTrackSelections.equals(currentTrackSelection)
+ || !newTracksInfo.equals(currentTracksInfo)) {
+ currentTrackSelection = newTrackSelections;
+ currentTrackGroups = newTrackGroups;
+ currentTracksInfo = newTracksInfo;
return true;
}
return false;
diff --git a/extensions/ima/src/test/java/com/google/android/exoplayer2/ext/ima/FakePlayer.java b/extensions/ima/src/test/java/com/google/android/exoplayer2/ext/ima/FakePlayer.java
index 66612a17a2..a958449b2a 100644
--- a/extensions/ima/src/test/java/com/google/android/exoplayer2/ext/ima/FakePlayer.java
+++ b/extensions/ima/src/test/java/com/google/android/exoplayer2/ext/ima/FakePlayer.java
@@ -21,6 +21,7 @@ import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.MediaItem;
import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.Timeline;
+import com.google.android.exoplayer2.TracksInfo;
import com.google.android.exoplayer2.testutil.StubExoPlayer;
import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
import com.google.android.exoplayer2.trackselection.TrackSelectionParameters;
@@ -240,6 +241,11 @@ import com.google.android.exoplayer2.util.ListenerSet;
return new TrackSelectionArray();
}
+ @Override
+ public TracksInfo getCurrentTracksInfo() {
+ return TracksInfo.EMPTY;
+ }
+
@Override
public TrackSelectionParameters getTrackSelectionParameters() {
return TrackSelectionParameters.DEFAULT_WITHOUT_CONTEXT;
diff --git a/library/common/src/main/java/com/google/android/exoplayer2/ForwardingPlayer.java b/library/common/src/main/java/com/google/android/exoplayer2/ForwardingPlayer.java
index 4fa547d2dc..c45c48495b 100644
--- a/library/common/src/main/java/com/google/android/exoplayer2/ForwardingPlayer.java
+++ b/library/common/src/main/java/com/google/android/exoplayer2/ForwardingPlayer.java
@@ -360,6 +360,11 @@ public class ForwardingPlayer implements Player {
return player.getCurrentTrackSelections();
}
+ @Override
+ public TracksInfo getCurrentTracksInfo() {
+ return player.getCurrentTracksInfo();
+ }
+
@Override
public TrackSelectionParameters getTrackSelectionParameters() {
return player.getTrackSelectionParameters();
@@ -645,6 +650,11 @@ public class ForwardingPlayer implements Player {
eventListener.onTracksChanged(trackGroups, trackSelections);
}
+ @Override
+ public void onTracksInfoChanged(TracksInfo tracksInfo) {
+ eventListener.onTracksInfoChanged(tracksInfo);
+ }
+
@Override
public void onMediaMetadataChanged(MediaMetadata mediaMetadata) {
eventListener.onMediaMetadataChanged(mediaMetadata);
diff --git a/library/common/src/main/java/com/google/android/exoplayer2/Player.java b/library/common/src/main/java/com/google/android/exoplayer2/Player.java
index 367194350b..96f6e12487 100644
--- a/library/common/src/main/java/com/google/android/exoplayer2/Player.java
+++ b/library/common/src/main/java/com/google/android/exoplayer2/Player.java
@@ -57,11 +57,8 @@ import java.util.List;
*
* - They can provide a {@link Timeline} representing the structure of the media being played,
* which can be obtained by calling {@link #getCurrentTimeline()}.
- *
- They can provide a {@link TrackGroupArray} defining the currently available tracks, which
- * can be obtained by calling {@link #getCurrentTrackGroups()}.
- *
- They can provide a {@link TrackSelectionArray} defining which of the currently available
- * tracks are selected to be rendered. This can be obtained by calling {@link
- * #getCurrentTrackSelections()}}.
+ *
- They can provide a {@link TracksInfo} defining the currently available tracks and which are
+ * selected to be rendered, which can be obtained by calling {@link #getCurrentTracksInfo()}.
*
*/
public interface Player {
@@ -122,10 +119,22 @@ public interface Player {
* concrete implementation may include null elements if it has a fixed number of renderer
* components, wishes to report a TrackSelection for each of them, and has one or more
* renderer components that is not assigned any selected tracks.
+ * @deprecated Use {@link #onTracksInfoChanged(TracksInfo)} instead.
*/
+ @Deprecated
default void onTracksChanged(
TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {}
+ /**
+ * Called when the available or selected tracks change.
+ *
+ * {@link #onEvents(Player, Events)} will also be called to report this event along with
+ * other events that happen in the same {@link Looper} message queue iteration.
+ *
+ * @param tracksInfo The available tracks information. Never null, but may be of length zero.
+ */
+ default void onTracksInfoChanged(TracksInfo tracksInfo) {}
+
/**
* Called when the combined {@link MediaMetadata} changes.
*
@@ -693,6 +702,7 @@ public interface Player {
COMMAND_SET_VIDEO_SURFACE,
COMMAND_GET_TEXT,
COMMAND_SET_TRACK_SELECTION_PARAMETERS,
+ COMMAND_GET_TRACK_INFOS,
};
private final FlagSet.Builder flagsBuilder;
@@ -923,8 +933,7 @@ public interface Player {
@Nullable MediaItem mediaItem, @MediaItemTransitionReason int reason) {}
@Override
- default void onTracksChanged(
- TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {}
+ default void onTracksInfoChanged(TracksInfo tracksInfo) {}
@Override
default void onIsLoadingChanged(boolean isLoading) {}
@@ -1278,7 +1287,7 @@ public interface Player {
int EVENT_TIMELINE_CHANGED = 0;
/** {@link #getCurrentMediaItem()} changed or the player started repeating the current item. */
int EVENT_MEDIA_ITEM_TRANSITION = 1;
- /** {@link #getCurrentTrackGroups()} or {@link #getCurrentTrackSelections()} changed. */
+ /** {@link #getCurrentTracksInfo()} changed. */
int EVENT_TRACKS_CHANGED = 2;
/** {@link #isLoading()} ()} changed. */
int EVENT_IS_LOADING_CHANGED = 3;
@@ -1331,8 +1340,8 @@ public interface Player {
* #COMMAND_CHANGE_MEDIA_ITEMS}, {@link #COMMAND_GET_AUDIO_ATTRIBUTES}, {@link
* #COMMAND_GET_VOLUME}, {@link #COMMAND_GET_DEVICE_VOLUME}, {@link #COMMAND_SET_VOLUME}, {@link
* #COMMAND_SET_DEVICE_VOLUME}, {@link #COMMAND_ADJUST_DEVICE_VOLUME}, {@link
- * #COMMAND_SET_VIDEO_SURFACE}, {@link #COMMAND_GET_TEXT} or {@link
- * #COMMAND_SET_TRACK_SELECTION_PARAMETERS}.
+ * #COMMAND_SET_VIDEO_SURFACE}, {@link #COMMAND_GET_TEXT}, {@link
+ * #COMMAND_SET_TRACK_SELECTION_PARAMETERS} or {@link #COMMAND_GET_TRACK_INFOS}.
*/
@Documented
@Retention(RetentionPolicy.SOURCE)
@@ -1366,6 +1375,7 @@ public interface Player {
COMMAND_SET_VIDEO_SURFACE,
COMMAND_GET_TEXT,
COMMAND_SET_TRACK_SELECTION_PARAMETERS,
+ COMMAND_GET_TRACK_INFOS,
})
@interface Command {}
/** Command to start, pause or resume playback. */
@@ -1424,6 +1434,8 @@ public interface Player {
int COMMAND_GET_TEXT = 27;
/** Command to set the player's track selection parameters. */
int COMMAND_SET_TRACK_SELECTION_PARAMETERS = 28;
+ /** Command to get track infos. */
+ int COMMAND_GET_TRACK_INFOS = 29;
/** Represents an invalid {@link Command}. */
int COMMAND_INVALID = -1;
@@ -1962,7 +1974,9 @@ public interface Player {
* Returns the available track groups.
*
* @see Listener#onTracksChanged(TrackGroupArray, TrackSelectionArray)
+ * @deprecated Use {@link #getCurrentTracksInfo()}.
*/
+ @Deprecated
TrackGroupArray getCurrentTrackGroups();
/**
@@ -1973,10 +1987,23 @@ public interface Player {
* components that is not assigned any selected tracks.
*
* @see Listener#onTracksChanged(TrackGroupArray, TrackSelectionArray)
+ * @deprecated Use {@link #getCurrentTracksInfo()}.
*/
+ @Deprecated
TrackSelectionArray getCurrentTrackSelections();
- /** Returns the parameters constraining the track selection. */
+ /**
+ * Returns the available tracks, as well as the tracks' support, type, and selection status.
+ *
+ * @see Listener#onTracksChanged(TrackGroupArray, TrackSelectionArray)
+ */
+ TracksInfo getCurrentTracksInfo();
+
+ /**
+ * Returns the parameters constraining the track selection.
+ *
+ * @see Listener#onTrackSelectionParametersChanged}
+ */
TrackSelectionParameters getTrackSelectionParameters();
/**
diff --git a/library/common/src/main/java/com/google/android/exoplayer2/TracksInfo.java b/library/common/src/main/java/com/google/android/exoplayer2/TracksInfo.java
new file mode 100644
index 0000000000..3f587ed8d0
--- /dev/null
+++ b/library/common/src/main/java/com/google/android/exoplayer2/TracksInfo.java
@@ -0,0 +1,300 @@
+/*
+ * Copyright (C) 2020 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;
+
+import static com.google.android.exoplayer2.util.Assertions.checkArgument;
+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.fromNullableBundle;
+import static com.google.android.exoplayer2.util.BundleableUtil.toBundleArrayList;
+
+import android.os.Bundle;
+import androidx.annotation.IntDef;
+import androidx.annotation.Nullable;
+import com.google.android.exoplayer2.source.TrackGroup;
+import com.google.android.exoplayer2.trackselection.TrackSelectionParameters;
+import com.google.common.base.MoreObjects;
+import com.google.common.collect.ImmutableList;
+import com.google.common.primitives.Booleans;
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
+import java.util.List;
+
+/** Immutable information ({@link TrackGroupInfo}) about tracks. */
+public final class TracksInfo implements Bundleable {
+ /**
+ * Information about tracks in a {@link TrackGroup}: their {@link C.TrackType}, if their format is
+ * supported by the player and if they are selected for playback.
+ */
+ public static final class TrackGroupInfo implements Bundleable {
+ private final TrackGroup trackGroup;
+ @C.FormatSupport private final int[] trackSupport;
+ private final @C.TrackType int trackType;
+ private final boolean[] trackSelected;
+
+ /**
+ * Constructs a TrackGroupInfo.
+ *
+ * @param trackGroup The {@link TrackGroup} described.
+ * @param trackSupport The {@link C.FormatSupport} of each track in the {@code trackGroup}.
+ * @param trackType The {@link C.TrackType} of the tracks in the {@code trackGroup}.
+ * @param tracksSelected Whether a track is selected for each track in {@code trackGroup}.
+ */
+ public TrackGroupInfo(
+ TrackGroup trackGroup,
+ @C.FormatSupport int[] trackSupport,
+ @C.TrackType int trackType,
+ boolean[] tracksSelected) {
+ int length = trackGroup.length;
+ checkArgument(length == trackSupport.length && length == tracksSelected.length);
+ this.trackGroup = trackGroup;
+ this.trackSupport = trackSupport.clone();
+ this.trackType = trackType;
+ this.trackSelected = tracksSelected.clone();
+ }
+
+ /** Returns the {@link TrackGroup} described by this {@code TrackGroupInfo}. */
+ public TrackGroup getTrackGroup() {
+ return trackGroup;
+ }
+
+ /**
+ * Returns the level of support for a track in a {@link TrackGroup}.
+ *
+ * @param trackIndex The index of the track in the {@link TrackGroup}.
+ * @return The {@link C.FormatSupport} of the track.
+ */
+ @C.FormatSupport
+ public int getTrackSupport(int trackIndex) {
+ return trackSupport[trackIndex];
+ }
+
+ /**
+ * Returns if a track in a {@link TrackGroup} is supported for playback.
+ *
+ * @param trackIndex The index of the track in the {@link TrackGroup}.
+ * @return True if the track's format can be played, false otherwise.
+ */
+ public boolean isTrackSupported(int trackIndex) {
+ return trackSupport[trackIndex] == C.FORMAT_HANDLED;
+ }
+
+ /** Returns if at least one track in a {@link TrackGroup} is selected for playback. */
+ public boolean isSelected() {
+ return Booleans.contains(trackSelected, true);
+ }
+
+ /** Returns if at least one track in a {@link TrackGroup} is supported. */
+ public boolean isSupported() {
+ for (int i = 0; i < trackSupport.length; i++) {
+ if (isTrackSupported(i)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns if a track in a {@link TrackGroup} is selected for playback.
+ *
+ *
Multiple tracks of a track group may be selected, in which case the the active one
+ * (currently playing) is undefined. This is common in adaptive streaming, where multiple tracks
+ * of different quality are selected and the active one changes depending on the network and the
+ * {@link TrackSelectionParameters}.
+ *
+ * @param trackIndex The index of the track in the {@link TrackGroup}.
+ * @return true if the track is selected, false otherwise.
+ */
+ public boolean isTrackSelected(int trackIndex) {
+ return trackSelected[trackIndex];
+ }
+
+ /**
+ * Returns the {@link C.TrackType} of the tracks in the {@link TrackGroup}. Tracks in a group
+ * are all of the same type.
+ */
+ public @C.TrackType int getTrackType() {
+ return trackType;
+ }
+
+ @Override
+ public boolean equals(@Nullable Object other) {
+ if (this == other) {
+ return true;
+ }
+ if (other == null || getClass() != other.getClass()) {
+ return false;
+ }
+ TrackGroupInfo that = (TrackGroupInfo) other;
+ return trackType == that.trackType
+ && trackGroup.equals(that.trackGroup)
+ && Arrays.equals(trackSupport, that.trackSupport)
+ && Arrays.equals(trackSelected, that.trackSelected);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = trackGroup.hashCode();
+ result = 31 * result + Arrays.hashCode(trackSupport);
+ result = 31 * result + trackType;
+ result = 31 * result + Arrays.hashCode(trackSelected);
+ return result;
+ }
+
+ // Bundleable implementation.
+ @Documented
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({
+ FIELD_TRACK_GROUP,
+ FIELD_TRACK_SUPPORT,
+ FIELD_TRACK_TYPE,
+ FIELD_TRACK_SELECTED,
+ })
+ private @interface FieldNumber {}
+
+ private static final int FIELD_TRACK_GROUP = 0;
+ private static final int FIELD_TRACK_SUPPORT = 1;
+ private static final int FIELD_TRACK_TYPE = 2;
+ private static final int FIELD_TRACK_SELECTED = 3;
+
+ @Override
+ public Bundle toBundle() {
+ Bundle bundle = new Bundle();
+ bundle.putBundle(keyForField(FIELD_TRACK_GROUP), trackGroup.toBundle());
+ bundle.putIntArray(keyForField(FIELD_TRACK_SUPPORT), trackSupport);
+ bundle.putInt(keyForField(FIELD_TRACK_TYPE), trackType);
+ bundle.putBooleanArray(keyForField(FIELD_TRACK_SELECTED), trackSelected);
+ return bundle;
+ }
+
+ /** Object that can restores a {@code TracksInfo} from a {@link Bundle}. */
+ public static final Creator CREATOR =
+ bundle -> {
+ TrackGroup trackGroup =
+ fromNullableBundle(
+ TrackGroup.CREATOR, bundle.getBundle(keyForField(FIELD_TRACK_GROUP)));
+ checkNotNull(trackGroup); // Can't create a trackGroup info without a trackGroup
+ @C.FormatSupport
+ final int[] trackSupport =
+ MoreObjects.firstNonNull(
+ bundle.getIntArray(keyForField(FIELD_TRACK_SUPPORT)), new int[trackGroup.length]);
+ @C.TrackType
+ int trackType = bundle.getInt(keyForField(FIELD_TRACK_TYPE), C.TRACK_TYPE_UNKNOWN);
+ boolean[] selected =
+ MoreObjects.firstNonNull(
+ bundle.getBooleanArray(keyForField(FIELD_TRACK_SELECTED)),
+ new boolean[trackGroup.length]);
+ return new TrackGroupInfo(trackGroup, trackSupport, trackType, selected);
+ };
+
+ private static String keyForField(@FieldNumber int field) {
+ return Integer.toString(field, Character.MAX_RADIX);
+ }
+ }
+
+ private final ImmutableList trackGroupInfos;
+
+ /** An empty {@code TrackInfo} containing no {@link TrackGroupInfo}. */
+ public static final TracksInfo EMPTY = new TracksInfo(ImmutableList.of());
+
+ /** Constructs {@code TracksInfo} from the provided {@link TrackGroupInfo}. */
+ public TracksInfo(List trackGroupInfos) {
+ this.trackGroupInfos = ImmutableList.copyOf(trackGroupInfos);
+ }
+
+ /** Returns the {@link TrackGroupInfo TrackGroupInfos}, describing each {@link TrackGroup}. */
+ public ImmutableList getTrackGroupInfos() {
+ return trackGroupInfos;
+ }
+
+ /** Returns if there is at least one track of type {@code trackType} but none are supported. */
+ public boolean isTypeSupportedOrEmpty(@C.TrackType int trackType) {
+ boolean supported = true;
+ for (int i = 0; i < trackGroupInfos.size(); i++) {
+ if (trackGroupInfos.get(i).trackType == trackType) {
+ if (trackGroupInfos.get(i).isSupported()) {
+ return true;
+ } else {
+ supported = false;
+ }
+ }
+ }
+ return supported;
+ }
+
+ /** Returns if at least one track of the type {@code trackType} is selected for playback. */
+ public boolean isTypeSelected(@C.TrackType int trackType) {
+ for (int i = 0; i < trackGroupInfos.size(); i++) {
+ TrackGroupInfo trackGroupInfo = trackGroupInfos.get(i);
+ if (trackGroupInfo.isSelected() && trackGroupInfo.getTrackType() == trackType) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean equals(@Nullable Object other) {
+ if (this == other) {
+ return true;
+ }
+ if (other == null || getClass() != other.getClass()) {
+ return false;
+ }
+ TracksInfo that = (TracksInfo) other;
+ return trackGroupInfos.equals(that.trackGroupInfos);
+ }
+
+ @Override
+ public int hashCode() {
+ return trackGroupInfos.hashCode();
+ }
+ // Bundleable implementation.
+
+ @Documented
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({
+ FIELD_TRACK_GROUP_INFOS,
+ })
+ private @interface FieldNumber {}
+
+ private static final int FIELD_TRACK_GROUP_INFOS = 0;
+
+ @Override
+ public Bundle toBundle() {
+ Bundle bundle = new Bundle();
+ bundle.putParcelableArrayList(
+ keyForField(FIELD_TRACK_GROUP_INFOS), toBundleArrayList(trackGroupInfos));
+ return bundle;
+ }
+
+ /** Object that can restore a {@code TracksInfo} from a {@link Bundle}. */
+ public static final Creator CREATOR =
+ bundle -> {
+ List trackGroupInfos =
+ fromBundleNullableList(
+ TrackGroupInfo.CREATOR,
+ bundle.getParcelableArrayList(keyForField(FIELD_TRACK_GROUP_INFOS)),
+ /* defaultValue= */ ImmutableList.of());
+ return new TracksInfo(trackGroupInfos);
+ };
+
+ private static String keyForField(@FieldNumber int field) {
+ return Integer.toString(field, Character.MAX_RADIX);
+ }
+}
diff --git a/library/common/src/test/java/com/google/android/exoplayer2/TracksInfoTest.java b/library/common/src/test/java/com/google/android/exoplayer2/TracksInfoTest.java
new file mode 100644
index 0000000000..3e285c9b35
--- /dev/null
+++ b/library/common/src/test/java/com/google/android/exoplayer2/TracksInfoTest.java
@@ -0,0 +1,111 @@
+/*
+ * 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;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import com.google.android.exoplayer2.source.TrackGroup;
+import com.google.common.collect.ImmutableList;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/** Unit test for {@link TracksInfo}. */
+@RunWith(AndroidJUnit4.class)
+public class TracksInfoTest {
+
+ @Test
+ public void roundTripViaBundle_ofEmptyTracksInfo_yieldsEqualInstance() {
+ TracksInfo before = TracksInfo.EMPTY;
+ TracksInfo after = TracksInfo.CREATOR.fromBundle(before.toBundle());
+ assertThat(after).isEqualTo(before);
+ }
+
+ @Test
+ public void roundTripViaBundle_ofTracksInfo_yieldsEqualInstance() {
+ TracksInfo before =
+ new TracksInfo(
+ ImmutableList.of(
+ new TracksInfo.TrackGroupInfo(
+ new TrackGroup(new Format.Builder().build()),
+ new int[] {C.FORMAT_EXCEEDS_CAPABILITIES},
+ C.TRACK_TYPE_AUDIO,
+ new boolean[] {true}),
+ new TracksInfo.TrackGroupInfo(
+ new TrackGroup(new Format.Builder().build(), new Format.Builder().build()),
+ new int[] {C.FORMAT_UNSUPPORTED_DRM, C.FORMAT_UNSUPPORTED_TYPE},
+ C.TRACK_TYPE_VIDEO,
+ new boolean[] {false, true})));
+ TracksInfo after = TracksInfo.CREATOR.fromBundle(before.toBundle());
+ assertThat(after).isEqualTo(before);
+ }
+
+ @Test
+ public void tracksInfoGetters_withoutTrack_returnExpectedValues() {
+ TracksInfo tracksInfo = new TracksInfo(ImmutableList.of());
+
+ assertThat(tracksInfo.isTypeSupportedOrEmpty(C.TRACK_TYPE_AUDIO)).isTrue();
+ assertThat(tracksInfo.isTypeSelected(C.TRACK_TYPE_AUDIO)).isFalse();
+ ImmutableList trackGroupInfos = tracksInfo.getTrackGroupInfos();
+ assertThat(trackGroupInfos).isEmpty();
+ }
+
+ @Test
+ public void tracksInfo_emptyStaticInstance_isEmpty() {
+ TracksInfo tracksInfo = TracksInfo.EMPTY;
+
+ assertThat(tracksInfo.getTrackGroupInfos()).isEmpty();
+ assertThat(tracksInfo).isEqualTo(new TracksInfo(ImmutableList.of()));
+ }
+
+ @Test
+ public void tracksInfoGetters_ofComplexTracksInfo_returnExpectedValues() {
+ TracksInfo.TrackGroupInfo trackGroupInfo0 =
+ new TracksInfo.TrackGroupInfo(
+ new TrackGroup(new Format.Builder().build()),
+ new int[] {C.FORMAT_EXCEEDS_CAPABILITIES},
+ C.TRACK_TYPE_AUDIO,
+ /* tracksSelected= */ new boolean[] {false});
+ TracksInfo.TrackGroupInfo trackGroupInfo1 =
+ new TracksInfo.TrackGroupInfo(
+ new TrackGroup(new Format.Builder().build(), new Format.Builder().build()),
+ new int[] {C.FORMAT_UNSUPPORTED_DRM, C.FORMAT_HANDLED},
+ C.TRACK_TYPE_VIDEO,
+ /* tracksSelected= */ new boolean[] {false, true});
+ TracksInfo tracksInfo = new TracksInfo(ImmutableList.of(trackGroupInfo0, trackGroupInfo1));
+
+ assertThat(tracksInfo.isTypeSupportedOrEmpty(C.TRACK_TYPE_AUDIO)).isFalse();
+ assertThat(tracksInfo.isTypeSupportedOrEmpty(C.TRACK_TYPE_VIDEO)).isTrue();
+ assertThat(tracksInfo.isTypeSupportedOrEmpty(C.TRACK_TYPE_TEXT)).isTrue();
+ assertThat(tracksInfo.isTypeSelected(C.TRACK_TYPE_AUDIO)).isFalse();
+ assertThat(tracksInfo.isTypeSelected(C.TRACK_TYPE_VIDEO)).isTrue();
+ ImmutableList trackGroupInfos = tracksInfo.getTrackGroupInfos();
+ assertThat(trackGroupInfos).hasSize(2);
+ assertThat(trackGroupInfos.get(0)).isSameInstanceAs(trackGroupInfo0);
+ assertThat(trackGroupInfos.get(1)).isSameInstanceAs(trackGroupInfo1);
+ assertThat(trackGroupInfos.get(0).isTrackSupported(0)).isFalse();
+ assertThat(trackGroupInfos.get(1).isTrackSupported(0)).isFalse();
+ assertThat(trackGroupInfos.get(1).isTrackSupported(1)).isTrue();
+ assertThat(trackGroupInfos.get(0).getTrackSupport(0)).isEqualTo(C.FORMAT_EXCEEDS_CAPABILITIES);
+ assertThat(trackGroupInfos.get(1).getTrackSupport(0)).isEqualTo(C.FORMAT_UNSUPPORTED_DRM);
+ assertThat(trackGroupInfos.get(1).getTrackSupport(1)).isEqualTo(C.FORMAT_HANDLED);
+ assertThat(trackGroupInfos.get(0).isTrackSelected(0)).isFalse();
+ assertThat(trackGroupInfos.get(1).isTrackSelected(0)).isFalse();
+ assertThat(trackGroupInfos.get(1).isTrackSelected(1)).isTrue();
+ assertThat(trackGroupInfos.get(0).getTrackType()).isEqualTo(C.TRACK_TYPE_AUDIO);
+ assertThat(trackGroupInfos.get(1).getTrackType()).isEqualTo(C.TRACK_TYPE_VIDEO);
+ }
+}
diff --git a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java
index c91d0b746a..d3d041ce93 100644
--- a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java
+++ b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java
@@ -196,6 +196,7 @@ import java.util.concurrent.CopyOnWriteArraySet;
new TrackSelectorResult(
new RendererConfiguration[renderers.length],
new ExoTrackSelection[renderers.length],
+ TracksInfo.EMPTY,
/* info= */ null);
period = new Timeline.Period();
permanentAvailableCommands =
@@ -210,7 +211,8 @@ import java.util.concurrent.CopyOnWriteArraySet;
COMMAND_GET_TIMELINE,
COMMAND_GET_MEDIA_ITEMS_METADATA,
COMMAND_SET_MEDIA_ITEMS_METADATA,
- COMMAND_CHANGE_MEDIA_ITEMS)
+ COMMAND_CHANGE_MEDIA_ITEMS,
+ COMMAND_GET_TRACK_INFOS)
.addIf(COMMAND_SET_TRACK_SELECTION_PARAMETERS, trackSelector.isSetParametersSupported())
.addAll(additionalPermanentAvailableCommands)
.build();
@@ -951,6 +953,11 @@ import java.util.concurrent.CopyOnWriteArraySet;
return new TrackSelectionArray(playbackInfo.trackSelectorResult.selections);
}
+ @Override
+ public TracksInfo getCurrentTracksInfo() {
+ return playbackInfo.trackSelectorResult.tracksInfo;
+ }
+
@Override
public TrackSelectionParameters getTrackSelectionParameters() {
return trackSelector.getParameters();
@@ -1274,6 +1281,9 @@ import java.util.concurrent.CopyOnWriteArraySet;
listeners.queueEvent(
Player.EVENT_TRACKS_CHANGED,
listener -> listener.onTracksChanged(newPlaybackInfo.trackGroups, newSelection));
+ listeners.queueEvent(
+ Player.EVENT_TRACKS_CHANGED,
+ listener -> listener.onTracksInfoChanged(newPlaybackInfo.trackSelectorResult.tracksInfo));
}
if (metadataChanged) {
final MediaMetadata finalMediaMetadata = mediaMetadata;
diff --git a/library/core/src/main/java/com/google/android/exoplayer2/SimpleExoPlayer.java b/library/core/src/main/java/com/google/android/exoplayer2/SimpleExoPlayer.java
index 00a5d66be0..eb5dc12060 100644
--- a/library/core/src/main/java/com/google/android/exoplayer2/SimpleExoPlayer.java
+++ b/library/core/src/main/java/com/google/android/exoplayer2/SimpleExoPlayer.java
@@ -1456,6 +1456,12 @@ public class SimpleExoPlayer extends BasePlayer
return player.getCurrentTrackSelections();
}
+ @Override
+ public TracksInfo getCurrentTracksInfo() {
+ verifyApplicationThread();
+ return player.getCurrentTracksInfo();
+ }
+
@Override
public TrackSelectionParameters getTrackSelectionParameters() {
verifyApplicationThread();
diff --git a/library/core/src/main/java/com/google/android/exoplayer2/analytics/AnalyticsCollector.java b/library/core/src/main/java/com/google/android/exoplayer2/analytics/AnalyticsCollector.java
index 1e908e41e4..4d1bcd457f 100644
--- a/library/core/src/main/java/com/google/android/exoplayer2/analytics/AnalyticsCollector.java
+++ b/library/core/src/main/java/com/google/android/exoplayer2/analytics/AnalyticsCollector.java
@@ -35,6 +35,7 @@ import com.google.android.exoplayer2.Player.PlaybackSuppressionReason;
import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.Timeline.Period;
import com.google.android.exoplayer2.Timeline.Window;
+import com.google.android.exoplayer2.TracksInfo;
import com.google.android.exoplayer2.analytics.AnalyticsListener.EventTime;
import com.google.android.exoplayer2.audio.AudioAttributes;
import com.google.android.exoplayer2.audio.AudioRendererEventListener;
@@ -586,6 +587,7 @@ public class AnalyticsCollector
}
@Override
+ @SuppressWarnings("deprecation") // Implementing and calling deprecate listener method
public final void onTracksChanged(
TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {
EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime();
@@ -595,6 +597,15 @@ public class AnalyticsCollector
listener -> listener.onTracksChanged(eventTime, trackGroups, trackSelections));
}
+ @Override
+ public void onTracksInfoChanged(TracksInfo tracksInfo) {
+ EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime();
+ sendEvent(
+ eventTime,
+ AnalyticsListener.EVENT_TRACKS_CHANGED,
+ listener -> listener.onTracksInfoChanged(eventTime, tracksInfo));
+ }
+
@SuppressWarnings("deprecation") // Calling deprecated listener method.
@Override
public final void onIsLoadingChanged(boolean isLoading) {
diff --git a/library/core/src/main/java/com/google/android/exoplayer2/analytics/AnalyticsListener.java b/library/core/src/main/java/com/google/android/exoplayer2/analytics/AnalyticsListener.java
index 0fca47cf56..9887f397ea 100644
--- a/library/core/src/main/java/com/google/android/exoplayer2/analytics/AnalyticsListener.java
+++ b/library/core/src/main/java/com/google/android/exoplayer2/analytics/AnalyticsListener.java
@@ -36,6 +36,7 @@ import com.google.android.exoplayer2.Player.DiscontinuityReason;
import com.google.android.exoplayer2.Player.PlaybackSuppressionReason;
import com.google.android.exoplayer2.Player.TimelineChangeReason;
import com.google.android.exoplayer2.Timeline;
+import com.google.android.exoplayer2.TracksInfo;
import com.google.android.exoplayer2.audio.AudioAttributes;
import com.google.android.exoplayer2.audio.AudioSink;
import com.google.android.exoplayer2.decoder.DecoderCounters;
@@ -221,7 +222,8 @@ public interface AnalyticsListener {
*/
int EVENT_MEDIA_ITEM_TRANSITION = Player.EVENT_MEDIA_ITEM_TRANSITION;
/**
- * {@link Player#getCurrentTrackGroups()} or {@link Player#getCurrentTrackSelections()} changed.
+ * {@link Player#getCurrentTracksInfo()}, {@link Player#getCurrentTrackGroups()} or {@link
+ * Player#getCurrentTrackSelections()} changed.
*/
int EVENT_TRACKS_CHANGED = Player.EVENT_TRACKS_CHANGED;
/** {@link Player#isLoading()} ()} changed. */
@@ -674,10 +676,20 @@ public interface AnalyticsListener {
* @param eventTime The event time.
* @param trackGroups The available tracks. May be empty.
* @param trackSelections The track selections for each renderer. May contain null elements.
+ * @deprecated Use {@link #onTracksInfoChanged}.
*/
+ @Deprecated
default void onTracksChanged(
EventTime eventTime, TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {}
+ /**
+ * Called when the available or selected tracks change.
+ *
+ * @param eventTime The event time.
+ * @param tracksInfo The available tracks information. Never null, but may be of length zero.
+ */
+ default void onTracksInfoChanged(EventTime eventTime, TracksInfo tracksInfo) {}
+
/**
* Called when the combined {@link MediaMetadata} changes.
*
diff --git a/library/core/src/main/java/com/google/android/exoplayer2/trackselection/MappingTrackSelector.java b/library/core/src/main/java/com/google/android/exoplayer2/trackselection/MappingTrackSelector.java
index c45a69f45f..0287bb39b2 100644
--- a/library/core/src/main/java/com/google/android/exoplayer2/trackselection/MappingTrackSelector.java
+++ b/library/core/src/main/java/com/google/android/exoplayer2/trackselection/MappingTrackSelector.java
@@ -21,6 +21,7 @@ import static java.lang.Math.min;
import android.util.Pair;
import androidx.annotation.IntDef;
import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.C.FormatSupport;
import com.google.android.exoplayer2.ExoPlaybackException;
@@ -30,11 +31,13 @@ import com.google.android.exoplayer2.RendererCapabilities.AdaptiveSupport;
import com.google.android.exoplayer2.RendererCapabilities.Capabilities;
import com.google.android.exoplayer2.RendererConfiguration;
import com.google.android.exoplayer2.Timeline;
+import com.google.android.exoplayer2.TracksInfo;
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.util.MimeTypes;
import com.google.android.exoplayer2.util.Util;
+import com.google.common.collect.ImmutableList;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -106,7 +109,7 @@ public abstract class MappingTrackSelector extends TrackSelector {
* renderer, track group and track (in that order).
* @param unmappedTrackGroups {@link TrackGroup}s not mapped to any renderer.
*/
- @SuppressWarnings("deprecation")
+ @VisibleForTesting
/* package */ MappedTrackInfo(
String[] rendererNames,
@C.TrackType int[] rendererTrackTypes,
@@ -144,7 +147,7 @@ public abstract class MappingTrackSelector extends TrackSelector {
*
* @see Renderer#getTrackType()
* @param rendererIndex The renderer index.
- * @return One of the {@code TRACK_TYPE_*} constants defined in {@link C}.
+ * @return The {@link C.TrackType} of the renderer.
*/
public @C.TrackType int getRendererType(int rendererIndex) {
return rendererTrackTypes[rendererIndex];
@@ -279,6 +282,7 @@ public abstract class MappingTrackSelector extends TrackSelector {
String firstSampleMimeType = null;
for (int i = 0; i < trackIndices.length; i++) {
int trackIndex = trackIndices[i];
+ @Nullable
String sampleMimeType =
rendererTrackGroups[rendererIndex].get(groupIndex).getFormat(trackIndex).sampleMimeType;
if (handledTrackCount++ == 0) {
@@ -406,7 +410,10 @@ public abstract class MappingTrackSelector extends TrackSelector {
rendererMixedMimeTypeAdaptationSupports,
periodId,
timeline);
- return new TrackSelectorResult(result.first, result.second, mappedTrackInfo);
+
+ TracksInfo tracksInfo = buildTracksInfo(result.second, mappedTrackInfo);
+
+ return new TrackSelectorResult(result.first, result.second, tracksInfo, mappedTrackInfo);
}
/**
@@ -536,4 +543,49 @@ public abstract class MappingTrackSelector extends TrackSelector {
}
return mixedMimeTypeAdaptationSupport;
}
+
+ @VisibleForTesting
+ /* package */ static TracksInfo buildTracksInfo(
+ @NullableType TrackSelection[] selections, MappedTrackInfo mappedTrackInfo) {
+ ImmutableList.Builder builder = new ImmutableList.Builder<>();
+ for (int rendererIndex = 0;
+ rendererIndex < mappedTrackInfo.getRendererCount();
+ rendererIndex++) {
+ TrackGroupArray trackGroupArray = mappedTrackInfo.getTrackGroups(rendererIndex);
+ @Nullable TrackSelection trackSelection = selections[rendererIndex];
+ for (int groupIndex = 0; groupIndex < trackGroupArray.length; groupIndex++) {
+ TrackGroup trackGroup = trackGroupArray.get(groupIndex);
+ @C.FormatSupport int[] trackSupport = new int[trackGroup.length];
+ boolean[] selected = new boolean[trackGroup.length];
+ for (int trackIndex = 0; trackIndex < trackGroup.length; trackIndex++) {
+ trackSupport[trackIndex] =
+ mappedTrackInfo.getTrackSupport(rendererIndex, groupIndex, trackIndex);
+ // Suppressing reference equality warning because the track group stored in the track
+ // selection must point to the exact track group object to be considered part of it.
+ @SuppressWarnings("ReferenceEquality")
+ boolean isTrackSelected =
+ (trackSelection != null)
+ && (trackSelection.getTrackGroup() == trackGroup)
+ && (trackSelection.indexOf(trackIndex) != C.INDEX_UNSET);
+ selected[trackIndex] = isTrackSelected;
+ }
+ @C.TrackType int trackGroupType = mappedTrackInfo.getRendererType(rendererIndex);
+ builder.add(
+ new TracksInfo.TrackGroupInfo(trackGroup, trackSupport, trackGroupType, selected));
+ }
+ }
+ TrackGroupArray unmappedTrackGroups = mappedTrackInfo.getUnmappedTrackGroups();
+ for (int groupIndex = 0; groupIndex < unmappedTrackGroups.length; groupIndex++) {
+ TrackGroup trackGroup = unmappedTrackGroups.get(groupIndex);
+ @C.FormatSupport int[] trackSupport = new int[trackGroup.length];
+ Arrays.fill(trackSupport, C.FORMAT_UNSUPPORTED_TYPE);
+ // A track group only contains tracks of the same type, thus only consider the first track.
+ @C.TrackType
+ int trackGroupType = MimeTypes.getTrackType(trackGroup.getFormat(0).sampleMimeType);
+ boolean[] selected = new boolean[trackGroup.length]; // Initialized to false.
+ builder.add(
+ new TracksInfo.TrackGroupInfo(trackGroup, trackSupport, trackGroupType, selected));
+ }
+ return new TracksInfo(builder.build());
+ }
}
diff --git a/library/core/src/main/java/com/google/android/exoplayer2/trackselection/TrackSelectorResult.java b/library/core/src/main/java/com/google/android/exoplayer2/trackselection/TrackSelectorResult.java
index e7f0caaedf..a7bef4324b 100644
--- a/library/core/src/main/java/com/google/android/exoplayer2/trackselection/TrackSelectorResult.java
+++ b/library/core/src/main/java/com/google/android/exoplayer2/trackselection/TrackSelectorResult.java
@@ -17,6 +17,7 @@ package com.google.android.exoplayer2.trackselection;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.RendererConfiguration;
+import com.google.android.exoplayer2.TracksInfo;
import com.google.android.exoplayer2.util.Util;
import org.checkerframework.checker.nullness.compatqual.NullableType;
@@ -32,6 +33,8 @@ public final class TrackSelectorResult {
public final @NullableType RendererConfiguration[] rendererConfigurations;
/** A {@link ExoTrackSelection} array containing the track selection for each renderer. */
public final @NullableType ExoTrackSelection[] selections;
+ /** Describe the tracks and which one were selected. */
+ public final TracksInfo tracksInfo;
/**
* An opaque object that will be returned to {@link TrackSelector#onSelectionActivated(Object)}
* should the selections be activated.
@@ -45,13 +48,34 @@ public final class TrackSelectorResult {
* @param info An opaque object that will be returned to {@link
* TrackSelector#onSelectionActivated(Object)} should the selection be activated. May be
* {@code null}.
+ * @deprecated Use {@link #TrackSelectorResult(RendererConfiguration[], ExoTrackSelection[],
+ * TracksInfo, Object)}.
*/
+ @Deprecated
public TrackSelectorResult(
@NullableType RendererConfiguration[] rendererConfigurations,
@NullableType ExoTrackSelection[] selections,
@Nullable Object info) {
+ this(rendererConfigurations, selections, TracksInfo.EMPTY, info);
+ }
+
+ /**
+ * @param rendererConfigurations A {@link RendererConfiguration} for each renderer. A null entry
+ * indicates the corresponding renderer should be disabled.
+ * @param selections A {@link ExoTrackSelection} array containing the selection for each renderer.
+ * @param tracksInfo Description of the available tracks and which one were selected.
+ * @param info An opaque object that will be returned to {@link
+ * TrackSelector#onSelectionActivated(Object)} should the selection be activated. May be
+ * {@code null}.
+ */
+ public TrackSelectorResult(
+ @NullableType RendererConfiguration[] rendererConfigurations,
+ @NullableType ExoTrackSelection[] selections,
+ TracksInfo tracksInfo,
+ @Nullable Object info) {
this.rendererConfigurations = rendererConfigurations;
this.selections = selections.clone();
+ this.tracksInfo = tracksInfo;
this.info = info;
length = rendererConfigurations.length;
}
diff --git a/library/core/src/test/java/com/google/android/exoplayer2/ExoPlayerTest.java b/library/core/src/test/java/com/google/android/exoplayer2/ExoPlayerTest.java
index 105a90471b..1de7410140 100644
--- a/library/core/src/test/java/com/google/android/exoplayer2/ExoPlayerTest.java
+++ b/library/core/src/test/java/com/google/android/exoplayer2/ExoPlayerTest.java
@@ -23,6 +23,7 @@ import static com.google.android.exoplayer2.Player.COMMAND_GET_DEVICE_VOLUME;
import static com.google.android.exoplayer2.Player.COMMAND_GET_MEDIA_ITEMS_METADATA;
import static com.google.android.exoplayer2.Player.COMMAND_GET_TEXT;
import static com.google.android.exoplayer2.Player.COMMAND_GET_TIMELINE;
+import static com.google.android.exoplayer2.Player.COMMAND_GET_TRACK_INFOS;
import static com.google.android.exoplayer2.Player.COMMAND_GET_VOLUME;
import static com.google.android.exoplayer2.Player.COMMAND_PLAY_PAUSE;
import static com.google.android.exoplayer2.Player.COMMAND_PREPARE_STOP;
@@ -8344,6 +8345,7 @@ public final class ExoPlayerTest {
assertThat(player.isCommandAvailable(COMMAND_SET_VIDEO_SURFACE)).isTrue();
assertThat(player.isCommandAvailable(COMMAND_GET_TEXT)).isTrue();
assertThat(player.isCommandAvailable(COMMAND_SET_TRACK_SELECTION_PARAMETERS)).isTrue();
+ assertThat(player.isCommandAvailable(COMMAND_GET_TRACK_INFOS)).isTrue();
}
@Test
@@ -11238,7 +11240,8 @@ public final class ExoPlayerTest {
COMMAND_ADJUST_DEVICE_VOLUME,
COMMAND_SET_VIDEO_SURFACE,
COMMAND_GET_TEXT,
- COMMAND_SET_TRACK_SELECTION_PARAMETERS);
+ COMMAND_SET_TRACK_SELECTION_PARAMETERS,
+ COMMAND_GET_TRACK_INFOS);
if (!isTimelineEmpty) {
builder.add(COMMAND_SEEK_TO_PREVIOUS);
}
diff --git a/library/core/src/test/java/com/google/android/exoplayer2/MediaPeriodQueueTest.java b/library/core/src/test/java/com/google/android/exoplayer2/MediaPeriodQueueTest.java
index 58d0d0c47b..635167e658 100644
--- a/library/core/src/test/java/com/google/android/exoplayer2/MediaPeriodQueueTest.java
+++ b/library/core/src/test/java/com/google/android/exoplayer2/MediaPeriodQueueTest.java
@@ -771,7 +771,10 @@ public final class MediaPeriodQueueTest {
mediaSourceList,
getNextMediaPeriodInfo(),
new TrackSelectorResult(
- new RendererConfiguration[0], new ExoTrackSelection[0], /* info= */ null));
+ new RendererConfiguration[0],
+ new ExoTrackSelection[0],
+ TracksInfo.EMPTY,
+ /* info= */ null));
}
private void clear() {
diff --git a/library/core/src/test/java/com/google/android/exoplayer2/analytics/AnalyticsCollectorTest.java b/library/core/src/test/java/com/google/android/exoplayer2/analytics/AnalyticsCollectorTest.java
index 0753464f04..4be75e1443 100644
--- a/library/core/src/test/java/com/google/android/exoplayer2/analytics/AnalyticsCollectorTest.java
+++ b/library/core/src/test/java/com/google/android/exoplayer2/analytics/AnalyticsCollectorTest.java
@@ -84,6 +84,7 @@ import com.google.android.exoplayer2.RenderersFactory;
import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.Timeline.Window;
+import com.google.android.exoplayer2.TracksInfo;
import com.google.android.exoplayer2.decoder.DecoderCounters;
import com.google.android.exoplayer2.drm.DefaultDrmSessionManager;
import com.google.android.exoplayer2.drm.DrmInitData;
@@ -100,7 +101,6 @@ import com.google.android.exoplayer2.source.LoadEventInfo;
import com.google.android.exoplayer2.source.MediaLoadData;
import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId;
-import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.source.ads.AdPlaybackState;
import com.google.android.exoplayer2.testutil.ActionSchedule;
import com.google.android.exoplayer2.testutil.ActionSchedule.PlayerRunnable;
@@ -115,7 +115,6 @@ import com.google.android.exoplayer2.testutil.FakeTimeline.TimelineWindowDefinit
import com.google.android.exoplayer2.testutil.FakeVideoRenderer;
import com.google.android.exoplayer2.testutil.TestExoPlayerBuilder;
import com.google.android.exoplayer2.testutil.TestUtil;
-import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
import com.google.android.exoplayer2.util.Clock;
import com.google.android.exoplayer2.util.ConditionVariable;
import com.google.android.exoplayer2.util.MimeTypes;
@@ -2224,8 +2223,7 @@ public final class AnalyticsCollectorTest {
}
@Override
- public void onTracksChanged(
- EventTime eventTime, TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {
+ public void onTracksInfoChanged(EventTime eventTime, TracksInfo tracksInfo) {
reportedEvents.add(new ReportedEvent(EVENT_TRACKS_CHANGED, eventTime));
}
diff --git a/library/core/src/test/java/com/google/android/exoplayer2/trackselection/DefaultTrackSelectorTest.java b/library/core/src/test/java/com/google/android/exoplayer2/trackselection/DefaultTrackSelectorTest.java
index 146e670798..e04a0530a7 100644
--- a/library/core/src/test/java/com/google/android/exoplayer2/trackselection/DefaultTrackSelectorTest.java
+++ b/library/core/src/test/java/com/google/android/exoplayer2/trackselection/DefaultTrackSelectorTest.java
@@ -38,6 +38,7 @@ import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.RendererCapabilities;
import com.google.android.exoplayer2.RendererConfiguration;
import com.google.android.exoplayer2.Timeline;
+import com.google.android.exoplayer2.TracksInfo;
import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId;
import com.google.android.exoplayer2.source.TrackGroup;
import com.google.android.exoplayer2.source.TrackGroupArray;
@@ -50,6 +51,7 @@ import com.google.android.exoplayer2.trackselection.TrackSelector.InvalidationLi
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;
@@ -1717,6 +1719,32 @@ public final class DefaultTrackSelectorTest {
assertFixedSelection(result.selections[0], trackGroups, formatAac);
}
+ /** Tests audio track selection when there are multiple audio renderers. */
+ @Test
+ public void selectTracks_multipleRenderer_allSelected() throws Exception {
+ RendererCapabilities[] rendererCapabilities =
+ new RendererCapabilities[] {VIDEO_CAPABILITIES, AUDIO_CAPABILITIES, AUDIO_CAPABILITIES};
+ TrackGroupArray trackGroups = new TrackGroupArray(AUDIO_TRACK_GROUP);
+
+ TrackSelectorResult result =
+ trackSelector.selectTracks(rendererCapabilities, trackGroups, periodId, TIMELINE);
+
+ assertThat(result.length).isEqualTo(3);
+ assertThat(result.rendererConfigurations)
+ .asList()
+ .containsExactly(null, DEFAULT, null)
+ .inOrder();
+ assertThat(result.selections[0]).isNull();
+ assertFixedSelection(result.selections[1], trackGroups, trackGroups.get(0).getFormat(0));
+ assertThat(result.selections[2]).isNull();
+ ImmutableList trackGroupInfos =
+ result.tracksInfo.getTrackGroupInfos();
+ assertThat(trackGroupInfos).hasSize(1);
+ assertThat(trackGroupInfos.get(0).getTrackGroup()).isEqualTo(AUDIO_TRACK_GROUP);
+ assertThat(trackGroupInfos.get(0).isTrackSelected(0)).isTrue();
+ assertThat(trackGroupInfos.get(0).getTrackSupport(0)).isEqualTo(FORMAT_HANDLED);
+ }
+
private static void assertSelections(TrackSelectorResult result, TrackSelection[] expected) {
assertThat(result.length).isEqualTo(expected.length);
for (int i = 0; i < expected.length; i++) {
diff --git a/library/core/src/test/java/com/google/android/exoplayer2/trackselection/MappingTrackSelectorTest.java b/library/core/src/test/java/com/google/android/exoplayer2/trackselection/MappingTrackSelectorTest.java
index 9a01f85aa9..ab1d3c14cc 100644
--- a/library/core/src/test/java/com/google/android/exoplayer2/trackselection/MappingTrackSelectorTest.java
+++ b/library/core/src/test/java/com/google/android/exoplayer2/trackselection/MappingTrackSelectorTest.java
@@ -27,12 +27,15 @@ import com.google.android.exoplayer2.RendererCapabilities.AdaptiveSupport;
import com.google.android.exoplayer2.RendererCapabilities.Capabilities;
import com.google.android.exoplayer2.RendererConfiguration;
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.MediaSource.MediaPeriodId;
import com.google.android.exoplayer2.source.TrackGroup;
import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.testutil.FakeTimeline;
import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.Util;
+import com.google.common.collect.ImmutableList;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -129,6 +132,61 @@ public final class MappingTrackSelectorTest {
return new TrackGroup(new Format.Builder().setSampleMimeType(sampleMimeType).build());
}
+ @Test
+ public void buildTrackInfos_withTestValues_isAsExpected() {
+ MappingTrackSelector.MappedTrackInfo mappedTrackInfo =
+ new MappingTrackSelector.MappedTrackInfo(
+ new String[] {"1", "2"},
+ new int[] {C.TRACK_TYPE_AUDIO, C.TRACK_TYPE_VIDEO},
+ new TrackGroupArray[] {
+ new TrackGroupArray(
+ new TrackGroup(new Format.Builder().build()),
+ new TrackGroup(new Format.Builder().build())),
+ new TrackGroupArray(
+ new TrackGroup(new Format.Builder().build(), new Format.Builder().build()))
+ },
+ new int[] {
+ RendererCapabilities.ADAPTIVE_SEAMLESS, RendererCapabilities.ADAPTIVE_NOT_SUPPORTED
+ },
+ new int[][][] {
+ new int[][] {new int[] {C.FORMAT_HANDLED}, new int[] {C.FORMAT_UNSUPPORTED_SUBTYPE}},
+ new int[][] {new int[] {C.FORMAT_UNSUPPORTED_DRM, C.FORMAT_EXCEEDS_CAPABILITIES}}
+ },
+ new TrackGroupArray(new TrackGroup(new Format.Builder().build())));
+ TrackSelection[] selections =
+ new TrackSelection[] {
+ new FixedTrackSelection(mappedTrackInfo.getTrackGroups(0).get(1), 0),
+ new FixedTrackSelection(mappedTrackInfo.getTrackGroups(1).get(0), 1)
+ };
+
+ TracksInfo tracksInfo = MappingTrackSelector.buildTracksInfo(selections, mappedTrackInfo);
+
+ ImmutableList trackGroupInfos = tracksInfo.getTrackGroupInfos();
+ assertThat(trackGroupInfos).hasSize(4);
+ assertThat(trackGroupInfos.get(0).getTrackGroup())
+ .isEqualTo(mappedTrackInfo.getTrackGroups(0).get(0));
+ assertThat(trackGroupInfos.get(1).getTrackGroup())
+ .isEqualTo(mappedTrackInfo.getTrackGroups(0).get(1));
+ assertThat(trackGroupInfos.get(2).getTrackGroup())
+ .isEqualTo(mappedTrackInfo.getTrackGroups(1).get(0));
+ assertThat(trackGroupInfos.get(3).getTrackGroup())
+ .isEqualTo(mappedTrackInfo.getUnmappedTrackGroups().get(0));
+ assertThat(trackGroupInfos.get(0).getTrackSupport(0)).isEqualTo(C.FORMAT_HANDLED);
+ assertThat(trackGroupInfos.get(1).getTrackSupport(0)).isEqualTo(C.FORMAT_UNSUPPORTED_SUBTYPE);
+ assertThat(trackGroupInfos.get(2).getTrackSupport(0)).isEqualTo(C.FORMAT_UNSUPPORTED_DRM);
+ assertThat(trackGroupInfos.get(2).getTrackSupport(1)).isEqualTo(C.FORMAT_EXCEEDS_CAPABILITIES);
+ assertThat(trackGroupInfos.get(3).getTrackSupport(0)).isEqualTo(C.FORMAT_UNSUPPORTED_TYPE);
+ assertThat(trackGroupInfos.get(0).isTrackSelected(0)).isFalse();
+ assertThat(trackGroupInfos.get(1).isTrackSelected(0)).isTrue();
+ assertThat(trackGroupInfos.get(2).isTrackSelected(0)).isFalse();
+ assertThat(trackGroupInfos.get(2).isTrackSelected(1)).isTrue();
+ assertThat(trackGroupInfos.get(3).isTrackSelected(0)).isFalse();
+ assertThat(trackGroupInfos.get(0).getTrackType()).isEqualTo(C.TRACK_TYPE_AUDIO);
+ assertThat(trackGroupInfos.get(1).getTrackType()).isEqualTo(C.TRACK_TYPE_AUDIO);
+ assertThat(trackGroupInfos.get(2).getTrackType()).isEqualTo(C.TRACK_TYPE_VIDEO);
+ assertThat(trackGroupInfos.get(3).getTrackType()).isEqualTo(C.TRACK_TYPE_UNKNOWN);
+ }
+
/**
* A {@link MappingTrackSelector} that stashes the {@link MappedTrackInfo} passed to {@link
* #selectTracks(MappedTrackInfo, int[][][], int[], MediaPeriodId, Timeline)}.
diff --git a/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/Transformer.java b/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/Transformer.java
index 7e7ead9440..7c6ba909c9 100644
--- a/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/Transformer.java
+++ b/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/Transformer.java
@@ -45,6 +45,7 @@ import com.google.android.exoplayer2.Renderer;
import com.google.android.exoplayer2.RenderersFactory;
import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.Timeline;
+import com.google.android.exoplayer2.TracksInfo;
import com.google.android.exoplayer2.analytics.AnalyticsListener;
import com.google.android.exoplayer2.audio.AudioRendererEventListener;
import com.google.android.exoplayer2.extractor.DefaultExtractorsFactory;
@@ -53,10 +54,8 @@ import com.google.android.exoplayer2.metadata.MetadataOutput;
import com.google.android.exoplayer2.source.DefaultMediaSourceFactory;
import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.source.MediaSourceFactory;
-import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.text.TextOutput;
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
-import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
import com.google.android.exoplayer2.util.Clock;
import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.Util;
@@ -636,8 +635,7 @@ public final class Transformer {
}
@Override
- public void onTracksChanged(
- EventTime eventTime, TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {
+ public void onTracksInfoChanged(EventTime eventTime, TracksInfo tracksInfo) {
if (muxerWrapper.getTrackCount() == 0) {
handleTransformationEnded(
new IllegalStateException(
diff --git a/library/ui/src/main/java/com/google/android/exoplayer2/ui/PlayerView.java b/library/ui/src/main/java/com/google/android/exoplayer2/ui/PlayerView.java
index d96170d4e2..53255ff69d 100644
--- a/library/ui/src/main/java/com/google/android/exoplayer2/ui/PlayerView.java
+++ b/library/ui/src/main/java/com/google/android/exoplayer2/ui/PlayerView.java
@@ -56,7 +56,7 @@ import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.Player.DiscontinuityReason;
import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.Timeline.Period;
-import com.google.android.exoplayer2.source.TrackGroupArray;
+import com.google.android.exoplayer2.TracksInfo;
import com.google.android.exoplayer2.text.Cue;
import com.google.android.exoplayer2.trackselection.TrackSelection;
import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
@@ -1511,7 +1511,7 @@ public class PlayerView extends FrameLayout implements AdViewProvider {
}
@Override
- public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray selections) {
+ public void onTracksInfoChanged(TracksInfo tracksInfo) {
// Suppress the update if transitioning to an unprepared period within the same window. This
// is necessary to avoid closing the shutter when such a transition occurs. See:
// https://github.com/google/ExoPlayer/issues/5507.
diff --git a/library/ui/src/main/java/com/google/android/exoplayer2/ui/StyledPlayerView.java b/library/ui/src/main/java/com/google/android/exoplayer2/ui/StyledPlayerView.java
index 72534209dc..6a30814673 100644
--- a/library/ui/src/main/java/com/google/android/exoplayer2/ui/StyledPlayerView.java
+++ b/library/ui/src/main/java/com/google/android/exoplayer2/ui/StyledPlayerView.java
@@ -57,7 +57,7 @@ import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.Player.DiscontinuityReason;
import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.Timeline.Period;
-import com.google.android.exoplayer2.source.TrackGroupArray;
+import com.google.android.exoplayer2.TracksInfo;
import com.google.android.exoplayer2.text.Cue;
import com.google.android.exoplayer2.trackselection.TrackSelection;
import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
@@ -1551,7 +1551,7 @@ public class StyledPlayerView extends FrameLayout implements AdViewProvider {
}
@Override
- public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray selections) {
+ public void onTracksInfoChanged(TracksInfo tracksInfo) {
// Suppress the update if transitioning to an unprepared period within the same window. This
// is necessary to avoid closing the shutter when such a transition occurs. See:
// https://github.com/google/ExoPlayer/issues/5507.
diff --git a/testutils/src/main/java/com/google/android/exoplayer2/testutil/ExoPlayerTestRunner.java b/testutils/src/main/java/com/google/android/exoplayer2/testutil/ExoPlayerTestRunner.java
index d3cd7fccaf..4096548a5c 100644
--- a/testutils/src/main/java/com/google/android/exoplayer2/testutil/ExoPlayerTestRunner.java
+++ b/testutils/src/main/java/com/google/android/exoplayer2/testutil/ExoPlayerTestRunner.java
@@ -38,9 +38,7 @@ import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.analytics.AnalyticsListener;
import com.google.android.exoplayer2.source.MediaSource;
-import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
-import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
import com.google.android.exoplayer2.upstream.BandwidthMeter;
import com.google.android.exoplayer2.util.Clock;
import com.google.android.exoplayer2.util.HandlerWrapper;
@@ -383,7 +381,6 @@ public final class ExoPlayerTestRunner implements Player.Listener, ActionSchedul
private SimpleExoPlayer player;
private Exception exception;
- private TrackGroupArray trackGroups;
private boolean playerWasPrepared;
private ExoPlayerTestRunner(
@@ -562,17 +559,6 @@ public final class ExoPlayerTestRunner implements Player.Listener, ActionSchedul
assertThat(playbackStates).containsExactlyElementsIn(states).inOrder();
}
- /**
- * Asserts that the last track group array reported by {@link
- * Player.Listener#onTracksChanged(TrackGroupArray, TrackSelectionArray)} is equal to the provided
- * track group array.
- *
- * @param trackGroupArray The expected {@link TrackGroupArray}.
- */
- public void assertTrackGroupsEqual(TrackGroupArray trackGroupArray) {
- assertThat(this.trackGroups).isEqualTo(trackGroupArray);
- }
-
/**
* Asserts that {@link Player.Listener#onPositionDiscontinuity(Player.PositionInfo,
* Player.PositionInfo, int)} was not called.
@@ -656,11 +642,6 @@ public final class ExoPlayerTestRunner implements Player.Listener, ActionSchedul
mediaItemTransitionReasons.add(reason);
}
- @Override
- public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {
- this.trackGroups = trackGroups;
- }
-
@Override
public void onPlaybackStateChanged(@Player.State int playbackState) {
playbackStates.add(playbackState);
diff --git a/testutils/src/main/java/com/google/android/exoplayer2/testutil/StubExoPlayer.java b/testutils/src/main/java/com/google/android/exoplayer2/testutil/StubExoPlayer.java
index 72efc2b81b..db307b52db 100644
--- a/testutils/src/main/java/com/google/android/exoplayer2/testutil/StubExoPlayer.java
+++ b/testutils/src/main/java/com/google/android/exoplayer2/testutil/StubExoPlayer.java
@@ -33,6 +33,7 @@ import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.PlayerMessage;
import com.google.android.exoplayer2.SeekParameters;
import com.google.android.exoplayer2.Timeline;
+import com.google.android.exoplayer2.TracksInfo;
import com.google.android.exoplayer2.audio.AudioAttributes;
import com.google.android.exoplayer2.audio.AuxEffectInfo;
import com.google.android.exoplayer2.source.MediaSource;
@@ -463,6 +464,11 @@ public class StubExoPlayer extends BasePlayer implements ExoPlayer {
throw new UnsupportedOperationException();
}
+ @Override
+ public TracksInfo getCurrentTracksInfo() {
+ throw new UnsupportedOperationException();
+ }
+
@Override
public TrackSelectionParameters getTrackSelectionParameters() {
throw new UnsupportedOperationException();